Repository: alex-petrenko/megaverse Branch: master Commit: c38436d69b7a Files: 205 Total size: 859.1 KB Directory structure: gitextract_gmyfvy_d/ ├── .dockerignore ├── .gitignore ├── .gitmodules ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── data/ │ ├── benchmarking.txt │ └── old_readme.md ├── docker/ │ ├── Dockerfile.base │ └── README.md ├── docker-compose.yml ├── environment.yml ├── megaverse/ │ ├── .gitignore │ ├── __init__.py │ ├── examples/ │ │ ├── __init__.py │ │ └── random_policy.py │ ├── megaverse_env.py │ └── tests/ │ ├── __init__.py │ └── test_env.py ├── megaverse_rl/ │ ├── __init__.py │ ├── enjoy_megaverse.py │ ├── megaverse_params.py │ ├── megaverse_utils.py │ ├── runs/ │ │ ├── __init__.py │ │ ├── megaverse_base_experiments.py │ │ ├── multi_agent.py │ │ ├── multitask.py │ │ ├── multitask_obstacles.py │ │ ├── performance_benchmark.py │ │ ├── performance_benchmark_all_envs.py │ │ ├── single_agent.py │ │ └── training_benchmark.py │ ├── sampling_benchmark.py │ ├── slurm/ │ │ ├── sbatch_template.sh │ │ └── slurm_cli.txt │ ├── tests/ │ │ ├── __init__.py │ │ └── test_megaverse_env.py │ └── train_megaverse.py ├── setup.cfg ├── setup.py └── src/ ├── 3rdparty/ │ ├── CMakeLists.txt │ └── glad/ │ ├── CMakeLists.txt │ ├── include/ │ │ ├── KHR/ │ │ │ └── khrplatform.h │ │ └── glad/ │ │ └── glad_egl.h │ └── src/ │ └── glad_egl.c ├── CMakeLists.txt ├── apps/ │ ├── CMakeLists.txt │ ├── mazegen.cpp │ ├── megaverse_test_app.cpp │ ├── viewer_app.cpp │ ├── viewer_args.cpp │ └── viewer_args.hpp ├── cmake/ │ ├── modules/ │ │ ├── FindCorrade.cmake │ │ ├── FindEGL.cmake │ │ ├── FindMagnum.cmake │ │ ├── FindMagnumIntegration.cmake │ │ └── FindSDL2.cmake │ └── util.cmake ├── examples/ │ ├── CMakeLists.txt │ ├── arcball/ │ │ ├── ArcBall.cpp │ │ ├── ArcBall.h │ │ ├── ArcBallCamera.h │ │ ├── ArcBallExample.cpp │ │ └── CMakeLists.txt │ ├── bullet_example/ │ │ ├── CMakeLists.txt │ │ └── bullet_example.cpp │ ├── picking_objects/ │ │ ├── CMakeLists.txt │ │ └── picking_example.cpp │ ├── textured_triangle/ │ │ ├── CMakeLists.txt │ │ ├── resources.conf │ │ ├── stone.tga │ │ ├── textured_triangle.cpp │ │ ├── textured_triangle_shader.cpp │ │ ├── textured_triangle_shader.frag │ │ ├── textured_triangle_shader.hpp │ │ └── textured_triangle_shader.vert │ └── triangle.cpp ├── libs/ │ ├── CMakeLists.txt │ ├── bindings/ │ │ ├── CMakeLists.txt │ │ └── megaverse.cpp │ ├── env/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ └── env/ │ │ │ ├── agent.hpp │ │ │ ├── const.hpp │ │ │ ├── env.hpp │ │ │ ├── env_renderer.hpp │ │ │ ├── kinematic_character_controller.hpp │ │ │ ├── physics.hpp │ │ │ ├── scenario.hpp │ │ │ ├── scenario_component.hpp │ │ │ ├── vector_env.hpp │ │ │ └── voxel_state.hpp │ │ └── src/ │ │ ├── agent.cpp │ │ ├── env.cpp │ │ ├── kinematic_character_controller.cpp │ │ └── vector_env.cpp │ ├── magnum_rendering/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ └── magnum_rendering/ │ │ │ ├── magnum_env_renderer.hpp │ │ │ └── rendering_context.hpp │ │ └── src/ │ │ ├── magnum_env_renderer.cpp │ │ └── rendering_context.cpp │ ├── mazes/ │ │ ├── CMakeLists.txt │ │ ├── LICENSE.txt │ │ ├── Readme.md │ │ ├── examples/ │ │ │ ├── gen.sh │ │ │ └── manual_input.xy │ │ ├── include/ │ │ │ └── mazes/ │ │ │ ├── breadthfirstsearch.h │ │ │ ├── cellborder.h │ │ │ ├── circularhexagonmaze.h │ │ │ ├── circularmaze.h │ │ │ ├── depthfirstsearch.h │ │ │ ├── hexagonalmaze.h │ │ │ ├── honeycombmaze.h │ │ │ ├── kruskal.h │ │ │ ├── looperasedrandomwalk.h │ │ │ ├── maze.h │ │ │ ├── prim.h │ │ │ ├── rectangularmaze.h │ │ │ ├── spanningtreealgorithm.h │ │ │ └── usermaze.h │ │ └── src/ │ │ ├── breadthfirstsearch.cpp │ │ ├── cellborder.cpp │ │ ├── circularhexagonmaze.cpp │ │ ├── circularmaze.cpp │ │ ├── depthfirstsearch.cpp │ │ ├── hexagonalmaze.cpp │ │ ├── honeycombmaze.cpp │ │ ├── kruskal.cpp │ │ ├── looperasedrandomwalk.cpp │ │ ├── maze.cpp │ │ ├── prim.cpp │ │ ├── rectangularmaze.cpp │ │ ├── spanningtreealgorithm.cpp │ │ └── usermaze.cpp │ ├── rendering/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ └── rendering/ │ │ │ └── render_utils.hpp │ │ └── src/ │ │ └── render_utils.cpp │ ├── scenarios/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ └── scenarios/ │ │ │ ├── component_fall_detection.hpp │ │ │ ├── component_hexagonal_maze.hpp │ │ │ ├── component_object_stacking.hpp │ │ │ ├── component_platforms.hpp │ │ │ ├── component_voxel_grid.hpp │ │ │ ├── const.hpp │ │ │ ├── init.hpp │ │ │ ├── layout_utils.hpp │ │ │ ├── platforms.hpp │ │ │ ├── scenario_box_a_gone.hpp │ │ │ ├── scenario_collect.hpp │ │ │ ├── scenario_default.hpp │ │ │ ├── scenario_empty.hpp │ │ │ ├── scenario_football.hpp │ │ │ ├── scenario_hex_explore.hpp │ │ │ ├── scenario_hex_memory.hpp │ │ │ ├── scenario_obstacles.hpp │ │ │ ├── scenario_rearrange.hpp │ │ │ ├── scenario_sokoban.hpp │ │ │ └── scenario_tower_building.hpp │ │ └── src/ │ │ ├── component_hexagonal_maze.cpp │ │ ├── layout_utils.cpp │ │ ├── scenario_box_a_gone.cpp │ │ ├── scenario_collect.cpp │ │ ├── scenario_empty.cpp │ │ ├── scenario_football.cpp │ │ ├── scenario_hex_explore.cpp │ │ ├── scenario_hex_memory.cpp │ │ ├── scenario_obstacles.cpp │ │ ├── scenario_rearrange.cpp │ │ ├── scenario_sokoban.cpp │ │ └── scenario_tower_building.cpp │ ├── util/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ └── util/ │ │ │ ├── argparse.hpp │ │ │ ├── enum.hpp │ │ │ ├── filesystem_utils.hpp │ │ │ ├── macro.hpp │ │ │ ├── magnum.hpp │ │ │ ├── math_utils.hpp │ │ │ ├── os_utils.hpp │ │ │ ├── perlin_noise.hpp │ │ │ ├── string_utils.hpp │ │ │ ├── tiny_logger.hpp │ │ │ ├── tiny_profiler.hpp │ │ │ ├── util.hpp │ │ │ └── voxel_grid.hpp │ │ └── src/ │ │ ├── filesystem_utils.cpp │ │ ├── string_utils.cpp │ │ ├── tiny_logger.cpp │ │ ├── tiny_profiler.cpp │ │ └── util.cpp │ ├── v4r_rendering/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ └── v4r_rendering/ │ │ │ └── v4r_env_renderer.hpp │ │ └── src/ │ │ └── v4r_env_renderer.cpp │ └── viewer/ │ ├── CMakeLists.txt │ ├── include/ │ │ └── viewer/ │ │ └── viewer.hpp │ └── src/ │ └── viewer.cpp └── test/ ├── CMakeLists.txt └── src/ ├── env_tests.cpp ├── filesystem_tests.cpp ├── gfx_tests.cpp ├── logger_tests.cpp ├── maze_tests.cpp ├── noise_test.cpp ├── string_tests.cpp ├── util_tests.cpp └── voxel_grid_tests.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ # Files for docker docker-compose.yml # Byte-compiled / optimized / DLL files __pycache__/ *.pyc *.pyo *.pyd *.local # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache/ .hypothesis/ coverage.xml # IPython Notebook .ipynb_checkpoints # pyenv .python-version # virtualenv venv/ ENV/ .env/ # pytest .pytest_cache/ # IDE .idea/ # mypy .mypy_cache/ # VCS .git/ .gitignore .gitattributes ================================================ FILE: .gitignore ================================================ # Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app # IDE .idea/** src/.idea/** # CMake build cmake-build* build* # Byte-compiled Python __pycache__/ # Python build stuff *.egg-info/** # RL-training related train_dir/ # Python intermediate folders megaverse/extension/** ================================================ FILE: .gitmodules ================================================ [submodule "src/3rdparty/googletest-1.10.0"] path = src/3rdparty/googletest-1.10.0 url = https://github.com/google/googletest.git [submodule "src/3rdparty/pybind11"] path = src/3rdparty/pybind11 url = https://github.com/pybind/pybind11.git [submodule "src/3rdparty/magnum"] path = src/3rdparty/magnum url = https://github.com/mosra/magnum.git [submodule "src/3rdparty/corrade"] path = src/3rdparty/corrade url = https://github.com/mosra/corrade.git [submodule "src/3rdparty/v4r"] path = src/3rdparty/v4r url = https://github.com/alex-petrenko/v4r.git [submodule "src/3rdparty/magnum-integration"] path = src/3rdparty/magnum-integration url = https://github.com/mosra/magnum-integration.git ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 Aleksei Petrenko Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MANIFEST.in ================================================ global-include * global-exclude *git* recursive-exclude **/benchmark* * recursive-exclude **/json/test * recursive-exclude **/.git * prune dist ================================================ FILE: Makefile ================================================ PYTHON ?= python export BASE_TAG=$(shell ${PYTHON} -c 'import hashlib; sha = hashlib.sha1((open("docker/Dockerfile.base").read() + open("requirements/requirements.txt").read()).encode()); print(sha.hexdigest())') BRANCH = $(shell git rev-parse --abbrev-ref HEAD) VERSION = $(shell git rev-parse --short HEAD) DATE = $(shell date +%F) export TAG=$(BRANCH)-$(VERSION)-$(DATE) .PHONY: pull build up push shell docker-bash pull-%: docker-compose pull $(subst pull-,,$@) build-%: docker-compose build $(subst build-,,$@) up-%: docker-compose up -d $(subst up-,,$@) push-%: docker-compose push $(subst push-,,$@) shell: @echo TAG=$(TAG) BASE_TAG=$(BASE_TAG) docker-bash: docker-compose run --it --rm dev bash .PHONY: upload upload-test clean upload: python3 -m twine upload --repository pypi dist/* upload-test: python3 -m twine upload --repository testpypi dist/* clean: rm -rf build dist ================================================ FILE: README.md ================================================ # Megaverse [](https://discord.gg/4ZNdhfaZtK) Megaverse is a dedicated high-throughput rendering and simulation engine for Artificial Intelligence research. It features an optimized batched renderer that enables generation of up to 1,000,000 observations per second on a single machine. * **Website:** [www.megaverse.info](https://www.megaverse.info) * **arXiv:** [arxiv.org/abs/2107.08170](https://arxiv.org/abs/2107.08170) Left: RL agent completing a TowerBuilding task. Right: human player solving a randomly generated obstacle course.

## Installation Currently, the easiest way to install Megaverse is to build directly from sources. ### Linux ```shell # 0) A completely clean Linux installation needs basic OpenGL libraries. The rest of the dependencies are installed with Conda and don't require elevated privileges. $ sudo apt install libgl1-mesa-dev libegl1-mesa-dev # 1) Install VulkanSDK from https://vulkan.lunarg.com/sdk/home#linux (download and unzip), or use the following commands: $ wget https://sdk.lunarg.com/sdk/download/1.2.198.1/linux/vulkansdk-linux-x86_64-1.2.198.1.tar.gz $ mkdir vulkansdk && tar -xzf vulkansdk-linux-x86_64-1.2.198.1.tar.gz --directory vulkansdk # 2) Add Vulkan SDK binaries to PATH (might need to do it each time recompiling Megaverse is required): $ cd vulkansdk/1.2.198.1 $ source ./setup-env.sh # 3) Clone the repo $ git clone https://github.com/alex-petrenko/megaverse.git # 4) Init submodules $ cd megaverse $ git submodule update --init --recursive # 5) Create a conda environment and install dependencies $ conda create --name megaverse python=3.9 $ conda activate megaverse (megaverse) $ conda install -c conda-forge 'opencv>=4.4,<4.5' 'cmake>=3.13' bullet cudatoolkit cudatoolkit-dev sdl2 # (alternatively you can boostrap from an environment file: conda env create -f environment.yml) # 6) Install megaverse into a conda env (megaverse) $ python setup.py develop (megaverse) $ pip install -e . # (Optional) 6.1) Build a .whl file to be installed elsewhere (megaverse) $ python setup.py bdist_wheel ``` ### macOS Although Vulkan-powered batched rendering is not supported on macOS, a limited OpenGL version can be built for local debugging and small-scale experiments on macOS. Installing on mac is very similar to Linux, sans any Vulkan/CUDA dependencies. ```shell # 1) Clone the repo $ git clone https://github.com/alex-petrenko/megaverse.git # 2) Init submodules $ cd megaverse $ git submodule update --init --recursive # 3) Create a conda environment and install dependencies $ conda create --name megaverse python=3.9 $ conda activate megaverse (megaverse) $ conda install -c conda-forge 'opencv>=4.4,<4.5' 'cmake>=3.13' bullet # 4) Install megaverse into a conda env (megaverse) $ python setup.py develop (megaverse) $ pip install -e . # (Optional) 4.1) Build a .whl file to be installed elsewhere (megaverse) $ python setup.py bdist_wheel ``` ### Docker Since installation does not require elevated priviliges, Docker setup is not required. However, Docker-based installation is also available, see here: [docker/README.md](https://github.com/alex-petrenko/megaverse/blob/master/docker/README.md). ## Examples ### Python API The following script executes a random policy: ```Python import numpy as np from megaverse.megaverse_env import MegaverseEnv env = MegaverseEnv( 'TowerBuilding', num_envs=2, num_agents_per_env=2, num_simulation_threads=4, use_vulkan=True, params={}, ) env.reset() while True: actions = [env.action_space.sample() for _ in range(env.num_agents)] obs, rewards, dones, infos = env.step(actions) if np.any(dones): break env.render() ``` ## RL Training Example training script using Sample Factory RL framework. First install the prerequisite: ``` pip install "sample-factory>=2.0" ``` (this instruction was tested on version from branch `sf2`, commit f4b5e971f467fc8dcabc0adee8b1c04885412fbb, `pip install git+https://github.com/alex-petrenko/sample-factory.git@f4b5e971f467fc8dcabc0adee8b1c04885412fbb`) Then, to train agents in the TowerBuilding environment, execute: ``` python -m megaverse_rl.train_megaverse --train_for_seconds=360000000 --train_for_env_steps=2000000000 --algo=APPO --gamma=0.997 --use_rnn=True --rnn_num_layers=2 --num_workers=8 --num_envs_per_worker=2 --num_epochs=1 --rollout=32 --recurrence=32 --batch_size=4096 --actor_worker_gpus 0 --num_policies=1 --with_pbt=False --max_grad_norm=0.0 --exploration_loss=symmetric_kl --exploration_loss_coeff=0.001 --megaverse_num_simulation_threads=1 --megaverse_num_envs_per_instance=48 --megaverse_num_agents_per_env=1 --megaverse_use_vulkan=True --policy_workers_per_policy=2 --reward_clip=30 --env=TowerBuilding --experiment=TowerBuilding ``` Observe the behavior of agents by running: ``` python -m megaverse_rl.enjoy --algo=APPO --env=TowerBuilding --experiment=TowerBuilding --megaverse_num_envs_per_instance=1 --fps=20 --megaverse_use_vulkan=True ``` See Sample Factory 2 documentation for additional information. ## Development ### Setting up a CMake project in IDE The core functionality of Megaverse is implemented in C++ and uses CMake build system. The easiest way to work on Megaverse C++ codebase is to use an IDE that can import a CMake project (defined by the root CMakeLists.txt in megaverse/src). Any such IDE would need to run `cmake` in order to build and debug the code. Thus `cmake` needs to be able to find all the libraries installed through conda (such as Bullet and OpenCV). The most straightforward way to make sure that libraries can be found is to start IDE directly from the conda environment we defined above (see section Installation/Linux). Specifically, for CLion IDE it would look like this: ```shell $ conda activate megaverse # navigate to Vulkan SDK installation dir (megaverse) $ cd vulkansdk-linux-x86_64-1.2.198.1/1.2.198.1/ # make sure that Vulkan env variables are initialized (megaverse) $ source ./setup-env.sh # start the IDE from the terminal (assuming clion is in PATH, usually you can do this with Tools->Create Command-Line Launcher) (megaverse) $ clion & ``` Now in the IDE open megaverse/src/CMakeLists.txt as CMake project and you should be able to build and run targets. #### Running an IDE without Conda enviroment Alternatively, if IDE is not run from a conda environment we might need to explicitly specify paths to libraries in IDE's CMake command line (i.e. in CLion that would be `Settings -> Build,Execution,Deployment -> CMake -> CMake options`). Your CMake options might looks like this: ``` -DPYTHON_EXECUTABLE=/home//miniconda3/envs/megaverse/bin/python -DCMAKE_CUDA_COMPILER=/home//miniconda3/envs/megaverse/bin/nvcc -DOpenCV_DIR=/home//miniconda3/envs/megaverse/lib/cmake/opencv4 -DBUILD_GUI_APPS=ON ``` Additionally, an environment variable `VULKAN_SDK=/home//all/libs/vulkansdk-linux-x86_64-1.2.198.1/1.2.198.1/x86_64` must be set. In most IDEs this can be set in the same CMake configuration dialogue. Finally, CMake should be able to find Bullet physics library. There are three ways to accomplish this: 1. Install `libbullet-dev` and CMake will find the system-wide installation 2. To link against `bullet` installed by Conda you need to make sure your IDE also uses Conda's `cmake`, rather than `cmake` bundled with the IDE. In CLion you can change this in `Settings -> Build,Execution,Deployment -> Toolchains -> CMake`. This way `cmake` should be able to find Conda's Bullet CMake config `/lib/cmake/bullet/BulletConfig.cmake` 3. Alternatively, you can build Bullet from sources and add a CMake option `-DBULLET_ROOT` pointing to the correct location. ### Notable build targets CMakeLists.txt defines many targets. The following targets are the most useful: * `megaverse` builds the overall project and the Python bindings (see setup.py) * `run_unit_tests` runs Google Tests (see `megaverse/src/test`) * `viewer_app` builds an interactive application that allows you to control agents with keyboard and explore environments with an overview camera. This one is really designed to interact with a single environment at a time, and is very useful during development and debugging phase. See details below. * `megaverse_test_app` can use the parallel simulation engine and batch renderer to execute many environment at once. See details below. ### Using viewer_app `viewer_app` can run any scenario in an interactive mode and offers a bunch of command line parameters: ``` Usage: viewer_app [options] Optional arguments: -h --help shows help message and exits [default: false] -v --version prints version information and exits [default: false] -l --list_scenarios list registered scenario names [default: false] --scenario name of the scenario to run [default: "ObstaclesEasy"] --num_agents size of the team, pass value 1 to have just a single agent [default: 2] --desired_fps rendering framerate for human perception; RL agents percieve the world at 15 FPS to avoid frameskip, hence the default value. [default: 15] --use_opengl Whether to use OpenGL renderer instead of fast Vulkan renderer (currently Vulkan is only supported in Linux) [default: false] ``` Once the app started, use keyboard to control the agent and the camera: * `WASD` and arrow keys to control the agent * `1,2,3,4,etc.` to switch between agents (if several are present in the environment) * Press `O` to toggle the overview camera, use mouse to control view angle * Use `UHJK` keys to control the position of the camera in the overview mode * Press `R` to reset the episode * Press `ENTER` to toggle Bullet collision debug view (only OpenGL version) * `ESC` to exit the app ### Using megaverse_test_app `megaverse_test_app` uses parallel interface and is a much easier target to debug compared to Python Gym API. ``` usage: megaverse_test_app [options] This app is designed to test the parallel execution engine and batched renderer by simulating multiple environments at once. This app uses pretty much the same interface as the Python Gym environment, sans the Python bindings. Whenever there is a problem with the environment, it is much easier to debug this app directly, rather than debugging the same code through Python. Example, render 12 agents at the same time: megaverse_test_app --scenario Collect --visualize --num_envs 4 --num_simulation_threads 1 --num_agents 3 --hires Some performance figures for future reference (on 10-core Intel i9): megaverse_test_app --scenario Empty --performance_test --num_envs 64 --num_simulation_threads 1 --num_agents 1 yields approximately 75000 FPS megaverse_test_app --scenario Collect --performance_test --num_envs 64 --num_simulation_threads 1 --num_agents 1 yields approximately 27000 FPS Optional arguments: -h --help shows help message and exits [default: false] -v --version prints version information and exits [default: false] -l --list_scenarios list registered scenario names [default: false] --scenario name of the scenario to run [default: "ObstaclesEasy"] --num_agents size of the team [default: 2] --use_opengl Whether to use OpenGL renderer instead of fast Vulkan renderer (currently Vulkan is only supported in Linux) [default: false] --num_envs number of parallel environments to simulate [default: 64] --num_simulation_threads number of parallel CPU threads to use for Bullet [default: 1] --visualize Whether to render multiple environments on screen [default: false] --visualize Whether to render multiple environments on screen [default: false] --delay_ms Delay between rendered frames in milliseconds. Use only with --visualize [default: 1] --performance_test Run for a limited number of env frames (currently 200000) to test performance. Uses random actions. [default: false] --hires Render at high resolution. Only use this parameter with --visualize and if the total number of agents is small [default: false] --user_actions Allows the user to control agents (otherwise will use randomly generated actions). Use only with --visualize [default: false] ``` ## Troubleshooting * A crash (segfault) on startup can be caused by the incorrect initialization of Vulkan device interface. Possible fixes: * `sudo apt remove mesa-vulkan-drivers` (unless other packages you require depend on this package) * Set envvar `export VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/nvidia_icd.json`, point to the location of `nvidia_icd.json` in your system. ## Citation If you use this repository in your work or otherwise wish to cite it, please make reference to our ICML2021 paper. ``` @inproceedings{petrenko2021megaverse, title={Megaverse: Simulating Embodied Agents at One Million Experiences per Second}, author={Petrenko, Aleksei and Wijmans, Erik and Shacklett, Brennan and Koltun, Vladlen}, booktitle={ICML}, year={2021} } ``` For questions, issues, inquiries please email apetrenko1991@gmail.com. Github issues and pull requests are welcome. ================================================ FILE: data/benchmarking.txt ================================================ July 29th, 2020 parallel -N0 './test_performance' ::: {1..20} 30x30 grid of cubes instanced, 128x72 11800 polygons 1 core: 7596 FPS 2 cores: 5815 * 2 = 11630 FPS 4 cores: 3852 * 4 = 15408 FPS 8 cores: 1950 * 8 = 15600 FPS 10 cores: 1546 * 10 = 15460 FPS 20 cores: 764 * 20 = 15280 FPS 10x10 grid of cubes instanced, 128x72 1200 polygons 1 core: 36000 FPS 2 cores: 9253 * 2 = 18506 FPS 3 cores: 6211 * 3 = 18633 FPS 4 cores: 4619 * 4 = 18476 FPS 10 cores: 1796 * 10 = 17960 FPS parallel -N0 './test_performance --magnum-device 2 --magnum-log verbose' ::: {1..10} 30x30 grid of cubes instanced, 128x72 11800 polygons software rendering 1 core: 440 FPS 2 cores: 481 * 2 = 962 FPS 4 cores: 468 * 4 = 1872 FPS 8 cores: 429 * 8 = 3432 FPS 10 cores: 411 * 10 = 4110 FPS 20 cores: 286 * 20 = 5720 FPS 10x10 grid of cubes instanced, 128x72 1200 polygons software rendering 1 core: 2464 FPS 2 cores: 2047 * 2 = 4094 FPS 4 cores: 2087 * 4 = 8348 FPS 10 cores: 1706 * 10 = 17060 FPS 20 cores: 1248 * 20 = 24960 FPS ================================================ FILE: data/old_readme.md ================================================ Previous Readme ``` 1) Clone the repo git clone https://github.com/alex-petrenko/megaverse.git 2) Init submodules git submodule update --init --recursive 3) Clone and build OpenCV (also possible to use OpenCV installed from Conda) cd ~/lib git clone https://github.com/opencv/opencv.git cd opencv mkdir build cmake -D CMAKE_BUILD_TYPE=Release .. make -j10 4) Install CUDA 10.2 https://developer.nvidia.com/cuda-10.2-download-archive 5) Install Vulkan wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.2.154-bionic.list https://packages.lunarg.com/vulkan/1.2.154/lunarg-vulkan-1.2.154-bionic.list sudo apt update sudo apt install vulkan-sdk (or install manually from https://vulkan.lunarg.com/sdk/home#linux, then source ./setup-env.sh to set envvars) 6) Setup Python environment (TODO: add environment.yml to this repo, currently using one from Sample Factory) (REQUIRES: opencv, cudatoolkit conda install -c conda-forge opencv conda install -c anaconda cudatoolkit conda install -c conda-forge cudatoolkit-dev ) git clone https://github.com/alex-petrenko/sample-factory.git cd sample-factory conda env create -f environment.yml conda activate sample-factory 7) Install PyBullet conda install -c conda-forge bullet (preferred) OR sudo apt install libbullet-dev 8) Update CMake if necessary (version 3.13 or newer is required) conda install -c anaconda cmake (preferred) OR sudo apt remove --purge cmake hash -r sudo snap install cmake --classic 9) Build the repo cd megaverse mkdir build cd build # Run CMake, specify the OpenCV build dir and Python3 executable (e.g. from a conda environment) cmake -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_CUDA_COMPILER=/usr/local/cuda/bin/nvcc \ -DPYTHON_EXECUTABLE:FILEPATH=/home//miniconda3/envs//bin/python3.7 \ ../src (optionally, add -DBULLET_ROOT=/home//lib/bullet3-2.89/install if you built it from sources) (optionally, add -DOpenCV_DIR=/home//lib/opencv/build if you built it from sources) # Build all targets make -j10 10) Run benchmark cd Release/bin ./megaverse_test_app (see global boolean flags in megaverse_test_app.cpp, they control the scenario and rendering settings TODO: make configurable) 11) Run viewer cd Release/bin ./viewer_app use WASD and arrows to control agent digits (1,2) to switch between agents in multi-agent envs E to interact with objects O to switch to overview camera UHJK to control overview camera (see global vars at the top of viewer_app.cpp file to control which environment is chosen TODO: make configurable) 12) To build the Python package and install it in the active Python env: python setup.py develop pip install -e . 13) Run tests python -m unittest 14) You are ready to use the Megaverse Python API! ``` Training: ``` Single experiment example: python -m megaverse_rl.train_megaverse --train_for_seconds=360000000 --train_for_env_steps=2000000000 --algo=APPO --gamma=0.997 --use_rnn=True --rnn_num_layers=2 --num_workers=12 --num_envs_per_worker=2 --num_epochs=1 --rollout=32 --recurrence=32 --batch_size=2048 --actor_worker_gpus 0 --num_policies=1 --with_pbt=False --max_grad_norm=0.0 --exploration_loss=symmetric_kl --exploration_loss_coeff=0.001 --voxel_num_simulation_threads=1 --voxel_use_vulkan=True --policy_workers_per_policy=2 --reward_clip=30 --env=voxel_env_TowerBuilding --experiment=test_cli Example runner script: python -m sample_factory.runner.run --run=megaverse_rl.runs.megaverse_single_agent --runner=processes --max_parallel=8 --pause_between=10 --experiments_per_gpu=2 --num_gpus=4 ``` Docker setup: ``` Install docker-compose: pip install docker-compose ``` ================================================ FILE: docker/Dockerfile.base ================================================ FROM nvidia/cudagl:10.2-devel-ubuntu18.04 # FROM nvidia/cudagl:11.0-devel-ubuntu20.04 # FROM nvidia/cuda:10.2-cudnn7-devel-ubuntu18.04 # Set up locale to prevent bugs with encoding ENV LC_ALL=C.UTF-8 LANG=C.UTF-8 RUN apt-get update || true && apt-get install -y \ libcudnn8 \ libglvnd0 libgl1 libglx0 libegl1 \ libglvnd-dev libgl1-mesa-dev libegl1-mesa-dev \ wget curl git zlib1g-dev \ libglib2.0-0 libsm6 libxext6 libxrender-dev \ python3 python3-pip cmake \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* WORKDIR /vulkan RUN wget https://sdk.lunarg.com/sdk/download/1.2.162.0/linux/vulkansdk-linux-x86_64-1.2.162.0.tar.gz && tar -xzf vulkansdk-linux-x86_64-1.2.162.0.tar.gz ENV VULKAN_SDK=/vulkan/1.2.162.0/x86_64 ENV PATH=${VULKAN_SDK}/bin:${PATH} ENV LD_LIBRARY_PATH=${VULKAN_SDK}/lib:${LD_LIBRARY_PATH:-} ENV VK_LAYER_PATH=${VULKAN_SDK}/etc/vulkan/explicit_layer.d RUN vulkaninfo WORKDIR / RUN cd /usr/bin \ && ln -s python3 python \ && ln -s pip3 pip RUN curl -LO http://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh && \ bash Miniconda3-latest-Linux-x86_64.sh -p /miniconda -b && \ rm Miniconda3-latest-Linux-x86_64.sh ENV PATH=/miniconda/bin:${PATH} RUN conda update -y conda && conda --version WORKDIR /workspace RUN git clone https://github.com/alex-petrenko/sample-factory.git RUN git clone https://github.com/alex-petrenko/megaverse.git WORKDIR sample-factory RUN conda env create -f environment.yml RUN conda init bash SHELL ["conda", "run", "-n", "sample-factory", "/bin/bash", "-c"] RUN conda install -n sample-factory -c anaconda cudatoolkit cmake RUN conda install -n sample-factory -c conda-forge opencv bullet cudatoolkit-dev RUN pip install -e . WORKDIR /workspace/megaverse RUN git submodule update --init --recursive RUN pip install -e . ================================================ FILE: docker/README.md ================================================ ### Docker instructions ``` 1) Clone the repo $ git clone https://github.com/alex-petrenko/megaverse.git 2) Build the image cd megaverse docker build -t megaverse -f docker/Dockerfile.base . 3) Start megaverse docker run --shm-size 8G --runtime=nvidia megaverse ./docker/run.sh (Optional) 4) Launch bash in the container and enjoy docker run -it --shm-size 8G --runtime=nvidia --entrypoint /bin/bash megaverse ``` ================================================ FILE: docker-compose.yml ================================================ version: "3.3" services: # base base: image: "${REPO}-base:${BASE_TAG}" build: context: . dockerfile: docker/Dockerfile.base # dev dev: image: "${REPO}-dev:${BASE_TAG}" depends_on: - base build: context: . dockerfile: docker/Dockerfile.dev args: REPO: ${REPO} BASE_TAG: ${BASE_TAG} volumes: - ".:/workspace" ================================================ FILE: environment.yml ================================================ name: megaverse channels: - pytorch - anaconda - conda-forge - defaults dependencies: - _libgcc_mutex=0.1=main - blas=1.0=mkl - bullet=2.89=py38hc5bc63f_1 - bzip2=1.0.8=h516909a_2 - ca-certificates=2020.10.14=0 - cairo=1.16.0=hcf35c78_1003 - certifi=2020.6.20=py38_0 - cmake=3.18.2=ha30ef3c_0 - cudatoolkit=10.2.89=hfd86e86_1 - cudatoolkit-dev=11.0.3=py38h497a2fe_2 - dbus=1.13.6=he372182_0 - expat=2.2.9=he1b5a44_2 - ffmpeg=4.3.1=h167e202_0 - fontconfig=2.13.1=h86ecdb6_1001 - freetype=2.10.2=h5ab3b9f_0 - gettext=0.19.8.1=hc5be6a0_1002 - glib=2.65.0=h6f030ca_0 - gmp=6.2.0=he1b5a44_2 - gnutls=3.6.13=h79a8f9a_0 - graphite2=1.3.13=he1b5a44_1001 - gst-plugins-base=1.14.5=h0935bb2_2 - gstreamer=1.14.5=h36ae1b5_2 - harfbuzz=2.4.0=h9f30f68_3 - hdf5=1.10.6=nompi_h3c11f04_101 - icu=64.2=he1b5a44_1 - intel-openmp=2020.1=217 - jasper=1.900.1=h07fcdf6_1006 - jpeg=9d=h516909a_0 - krb5=1.18.2=h173b8e3_0 - lame=3.100=h14c3975_1001 - lcms2=2.11=h396b838_0 - ld_impl_linux-64=2.34=hc38a660_9 - libblas=3.8.0=16_mkl - libcblas=3.8.0=16_mkl - libclang=9.0.1=default_hde54327_0 - libcurl=7.71.1=h20c2e04_1 - libedit=3.1.20191231=h14c3975_1 - libffi=3.2.1=he1b5a44_1007 - libgcc-ng=9.1.0=hdf63c60_0 - libgfortran-ng=7.5.0=hdf63c60_14 - libiconv=1.15=h516909a_1006 - liblapack=3.8.0=16_mkl - liblapacke=3.8.0=16_mkl - libllvm9=9.0.1=he513fc3_1 - libopencv=4.4.0=py38_2 - libpng=1.6.37=hbc83047_0 - libssh2=1.9.0=h1ba5d50_1 - libstdcxx-ng=9.1.0=hdf63c60_0 - libtiff=4.1.0=h2733197_1 - libuuid=2.32.1=h14c3975_1000 - libuv=1.40.0=h7b6447c_0 - libwebp-base=1.1.0=h516909a_3 - libxcb=1.13=h14c3975_1002 - libxkbcommon=0.10.0=he1b5a44_0 - libxml2=2.9.10=hee79883_0 - lz4-c=1.9.2=he6710b0_1 - mkl=2020.1=217 - mkl-service=2.3.0=py38he904b0f_0 - mkl_fft=1.1.0=py38h23d657b_0 - mkl_random=1.1.1=py38h0573a6f_0 - ncurses=6.2=he6710b0_1 - nettle=3.4.1=h1bed415_1002 - ninja=1.10.0=py38hfd86e86_0 - nspr=4.27=he1b5a44_1 - nss=3.55=he751ad9_0 - numpy=1.19.1=py38hbc911f0_0 - numpy-base=1.19.1=py38hfa32c7d_0 - olefile=0.46=py_0 - opencv=4.4.0=py38_2 - openh264=2.1.1=h8b12597_0 - openssl=1.1.1h=h7b6447c_0 - pcre=8.44=he1b5a44_0 - pillow=7.2.0=py38hb39fc2d_0 - pip=20.2.1=py38_0 - pixman=0.38.0=h516909a_1003 - pthread-stubs=0.4=h14c3975_1001 - py-opencv=4.4.0=py38h23f93f0_2 - python=3.8.5=h4d41432_2_cpython - python_abi=3.8=1_cp38 - pytorch=1.6.0=py3.8_cuda10.2.89_cudnn7.6.5_0 - qt=5.12.5=hd8c4c69_1 - readline=8.0=h7b6447c_0 - rhash=1.4.0=h1ba5d50_0 - setuptools=49.2.0=py38_0 - six=1.15.0=py_0 - sqlite=3.32.3=h62c20be_0 - tk=8.6.10=hbc83047_0 - torchvision=0.7.0=py38_cu102 - wheel=0.34.2=py38_0 - x264=1!152.20180806=h14c3975_0 - xorg-kbproto=1.0.7=h14c3975_1002 - xorg-libice=1.0.10=h516909a_0 - xorg-libsm=1.2.3=h84519dc_1000 - xorg-libx11=1.6.11=h516909a_0 - xorg-libxau=1.0.9=h14c3975_0 - xorg-libxdmcp=1.1.3=h516909a_0 - xorg-libxext=1.3.4=h516909a_0 - xorg-libxrender=0.9.10=h516909a_1002 - xorg-renderproto=0.11.1=h14c3975_1002 - xorg-xextproto=7.3.0=h14c3975_1002 - xorg-xproto=7.0.31=h14c3975_1007 - xz=5.2.5=h7b6447c_0 - zlib=1.2.11=h7b6447c_3 - zstd=1.4.5=h9ceee32_0 - pip: - absl-py==0.9.0 - atari-py==0.2.6 - cloudpickle==1.3.0 - colorlog==4.2.1 - cython==0.29.23 - faster-fifo==1.2.0 - filelock==3.0.12 - future==0.18.2 - grpcio==1.31.0 - gym==0.17.2 - markdown==3.2.2 - opencv-python==4.5.2.54 - protobuf==3.12.4 - psutil==5.7.2 - pyglet==1.5.0 - scipy==1.5.2 - tensorboard==1.15.0 - tensorboardx==2.0 - threadpoolctl==2.1.0 - werkzeug==1.0.1 prefix: /home/boyuan/miniconda3/envs/megaverse ================================================ FILE: megaverse/.gitignore ================================================ .idea/** __pycache__ ================================================ FILE: megaverse/__init__.py ================================================ ================================================ FILE: megaverse/examples/__init__.py ================================================ ================================================ FILE: megaverse/examples/random_policy.py ================================================ import numpy as np from megaverse.megaverse_env import MegaverseEnv env = MegaverseEnv( 'ObstaclesHard', num_envs=2, num_agents_per_env=2, num_simulation_threads=4, use_vulkan=True, params={}, ) env.reset() while True: actions = [env.action_space.sample() for _ in range(env.num_agents)] obs, rewards, dones, infos = env.step(actions) if np.any(dones): break env.render() env.close() ================================================ FILE: megaverse/megaverse_env.py ================================================ import cv2 import gym import numpy as np from gym.spaces import Discrete # noinspection PyUnresolvedReferences from megaverse.extension.megaverse import MegaverseGym, set_megaverse_log_level MEGAVERSE8 = [ 'TowerBuilding', 'ObstaclesEasy', 'ObstaclesHard', 'Collect', 'Sokoban', 'HexMemory', 'HexExplore', 'Rearrange', ] OBSTACLES_MULTITASK = [ 'ObstaclesWalls', 'ObstaclesSteps', 'ObstaclesLava', 'ObstaclesEasy', 'ObstaclesHard', ] def make_env_multitask(multitask_name, task_idx, num_envs, num_agents_per_env, num_simulation_threads, use_vulkan=False, params=None): assert 'multitask' in multitask_name if multitask_name.endswith('megaverse8'): tasks = MEGAVERSE8 elif multitask_name.endswith('obstacles'): tasks = OBSTACLES_MULTITASK else: raise NotImplementedError() scenario_idx = task_idx % len(tasks) scenario = tasks[scenario_idx] print('Multi-task, scenario', scenario_idx, scenario) return MegaverseEnv(scenario, num_envs, num_agents_per_env, num_simulation_threads, use_vulkan, params) class MegaverseEnv(gym.Env): def __init__(self, scenario_name, num_envs, num_agents_per_env, num_simulation_threads, use_vulkan=False, params=None): scenario_name = scenario_name.casefold() self.scenario_name = scenario_name self.is_multiagent = True set_megaverse_log_level(2) self.img_w = 128 self.img_h = 72 self.channels = 3 self.use_vulkan = use_vulkan # total number of simulated agents self.num_agents = num_envs * num_agents_per_env self.num_envs = num_envs self.num_agents_per_env = num_agents_per_env float_params = {} if params is not None: for k, v in params.items(): if isinstance(v, float): float_params[k] = v else: raise Exception('Params of type %r not supported', type(v)) # float_params['episodeLengthSec'] = 1.0 self.env = MegaverseGym( self.scenario_name, self.img_w, self.img_h, num_envs, num_agents_per_env, num_simulation_threads, use_vulkan, float_params, ) # obtaining default reward shaping scheme self.default_shaping_scheme = self.env.get_reward_shaping(0, 0) self.action_space = self.generate_action_space(self.env.action_space_sizes()) self.observation_space = gym.spaces.Box(0, 255, (self.channels, self.img_h, self.img_w), dtype=np.uint8) @staticmethod def generate_action_space(action_space_sizes): """ Left = 1 << 1, Right = 1 << 2, Forward = 1 << 3, Backward = 1 << 4, LookLeft = 1 << 5, LookRight = 1 << 6, Jump = 1 << 7, Interact = 1 << 8, LookDown = 1 << 9, LookUp = 1 << 10, """ # spaces = [ # Discrete(3), # noop, go left, go right # Discrete(3), # noop, forward, backward # Discrete(3), # noop, look left, look right # Discrete(2), # noop, jump # Discrete(2), # noop, interact # Discrete(3), # noop, look down, look up # ] spaces = [Discrete(sz) for sz in action_space_sizes] space = gym.spaces.Tuple(spaces) return space def seed(self, seed=None): if seed is None: return assert isinstance(seed, int), 'Expect seed to be an integer' self.env.seed(seed) def observations(self): obs = [] for env_i in range(self.num_envs): for agent_i in range(self.num_agents_per_env): o = self.env.get_observation(env_i, agent_i) o = o[:, :, :3] o = np.transpose(o, (2, 0, 1)) # convert to CHW for PyTorch obs.append(o) return obs def reset(self): self.env.reset() return self.observations() def step(self, actions): action_idx = 0 for env_i in range(self.num_envs): for agent_i in range(self.num_agents_per_env): self.env.set_actions(env_i, agent_i, actions[action_idx]) action_idx += 1 self.env.step() dones, infos = [], [] agent_i = 0 for env_i in range(self.num_envs): done = self.env.is_done(env_i) # currently no individual done per agent dones.extend([done for _ in range(self.num_agents_per_env)]) if done: infos.extend([dict(true_reward=float(self.env.true_objective(env_i, j))) for j in range(self.num_agents_per_env)]) else: infos.extend([{} for _ in range(self.num_agents_per_env)]) agent_i += self.num_agents_per_env rewards = self.env.get_last_rewards() obs = self.observations() return obs, rewards, dones, infos def convert_obs(self, obs): if not self.use_vulkan: obs = cv2.flip(obs, 0) obs = cv2.cvtColor(obs, cv2.COLOR_RGB2BGR) return obs def render(self, mode='human'): self.env.draw_overview() self.env.draw_hires() rows = [] for env_i in range(self.num_envs): obs = [self.convert_obs(self.env.get_hires_observation(env_i, i)) for i in range(self.num_agents_per_env)] obs_concat = np.concatenate(obs, axis=1) rows.append(obs_concat) obs_final = np.concatenate(rows, axis=0) cv2.imshow(f'agent_{id(self)}', obs_final) cv2.waitKey(1) return obs_final def get_default_reward_shaping(self): return self.default_shaping_scheme def get_current_reward_shaping(self, actor_idx: int): env_idx = actor_idx // self.num_agents_per_env agent_idx = actor_idx % self.num_agents_per_env return self.env.get_reward_shaping(env_idx, agent_idx) def set_reward_shaping(self, reward_shaping: dict, actor_idx: int): env_idx = actor_idx // self.num_agents_per_env agent_idx = actor_idx % self.num_agents_per_env return self.env.set_reward_shaping(env_idx, agent_idx, reward_shaping) def close(self): if self.env: self.env.close() ================================================ FILE: megaverse/tests/__init__.py ================================================ ================================================ FILE: megaverse/tests/test_env.py ================================================ import copy import os import time import numpy as np from unittest import TestCase from megaverse.megaverse_env import MegaverseEnv, make_env_multitask def sample_actions(e): return [e.action_space.sample() for _ in range(e.num_agents)] def make_test_env(num_envs, num_agents_per_env, num_simulation_threads, use_vulkan=False, params=None): """Making env with a default scenario name.""" return MegaverseEnv('ObstaclesEasy', num_envs, num_agents_per_env, num_simulation_threads, use_vulkan, params) class TestEnv(TestCase): def test_env(self): e = make_test_env(num_envs=1, num_agents_per_env=1, num_simulation_threads=1) o = e.reset() o = e.step(sample_actions(e)) e.close() def test_env_close_immediately(self): e = make_test_env(1, 1, 1) e.close() def test_two_envs_same_process(self): e1 = make_test_env(1, 1, 1) e2 = make_test_env(1, 1, 1) e1.reset() e2.reset() e1.close() e2.close() def test_seeds(self): e1 = make_test_env(1, 1, 1) e1.seed(42) e2 = make_test_env(1, 1, 1) e2.seed(42) obs1 = e1.reset() obs2 = e2.reset() self.assertTrue(np.array_equal(obs1, obs2)) e2.close() e1.close() # after this we have randomness due to physics? def rendering(self, use_vulkan, episode_length_sec=60.0): params = {'episodeLengthSec': episode_length_sec} e1 = make_test_env(num_envs=2, num_agents_per_env=2, num_simulation_threads=2, use_vulkan=use_vulkan, params=params) e2 = make_test_env(num_envs=1, num_agents_per_env=1, num_simulation_threads=1, use_vulkan=use_vulkan, params=params) e1.reset() e2.reset() e1.render() e2.render() for i in range(100): e1.step(sample_actions(e1)) e1.render() e2.step(sample_actions(e2)) e2.render() e2.close() e1.close() def test_render(self): self.rendering(use_vulkan=False) def test_render_vulkan(self): self.rendering(use_vulkan=True) def test_render_reset(self): self.rendering(use_vulkan=False, episode_length_sec=1.0) def test_render_vulkan_reset(self): self.rendering(use_vulkan=True, episode_length_sec=1.0) @staticmethod def performance_num_envs(n, n_steps=5000): print(f'Performance {n} {n_steps}') envs = [make_test_env(1, 1, 1, use_vulkan=True) for _ in range(n)] for e in envs: e.seed(42) e.reset() total_reward = 0.0 actions = sample_actions(envs[0]) start = time.time() for step in range(n_steps): for i, e in enumerate(envs): obs, rew, dones, infos = e.step(actions) total_reward += sum(rew) if all(dones): print(f'Episode boundary env {i}') end = time.time() elapsed = end - start fps = envs[0].num_agents * n * n_steps / elapsed print(f'Time {elapsed:.3f}, fps: {fps:.1f}, total reward: {total_reward:.3f}') for e in envs: e.close() return fps def test_performance(self): fps1 = self.performance_num_envs(1) fps2 = self.performance_num_envs(2) fps4 = self.performance_num_envs(4) # print(fps1, fps2, fps4) def test_reward_shaping(self): e = MegaverseEnv('TowerBuilding', num_envs=3, num_agents_per_env=2, num_simulation_threads=2, use_vulkan=True) default_reward_shaping = e.get_default_reward_shaping() self.assertEqual(default_reward_shaping, e.get_current_reward_shaping(0)) self.assertEqual(default_reward_shaping, e.get_current_reward_shaping(1)) self.assertEqual(default_reward_shaping, e.get_current_reward_shaping(2)) self.assertEqual(default_reward_shaping, e.get_current_reward_shaping(5)) new_reward_shaping = copy.deepcopy(default_reward_shaping) for k, v in new_reward_shaping.items(): new_reward_shaping[k] = v * 3 e.set_reward_shaping(new_reward_shaping, 3) self.assertEqual(default_reward_shaping, e.get_current_reward_shaping(0)) self.assertEqual(default_reward_shaping, e.get_current_reward_shaping(1)) self.assertNotEqual(default_reward_shaping, e.get_current_reward_shaping(3)) e.close() def test_memleak(self): def mem_usage_kb(): import psutil process = psutil.Process(os.getpid()) return process.memory_info().rss / 1024 # params = {'episodeLengthSec': 0.1} params = {} e = MegaverseEnv('Rearrange', num_envs=32, num_agents_per_env=1, num_simulation_threads=1, use_vulkan=True, params=params) e.reset() orig_mem_usage = mem_usage_kb() for i in range(1000): print('Mem difference: ', mem_usage_kb() - orig_mem_usage, 'kb') e.step(sample_actions(e)) print('Final mem difference: ', mem_usage_kb() - orig_mem_usage, 'kb') e.close() def test_multitask(self): import multiprocessing as mp num_processes = 2 def run_single_task(i): e = make_env_multitask('megaverse8', i, 1, 1, 1, use_vulkan=True, params={}) e.reset() e.render() # TODO: if this call is omitted we have rendering bugs. Fixme! for _ in range(1000): e.step(sample_actions(e)) e.render() e.close() processes = [] for process_idx in range(num_processes): p = mp.Process(target=run_single_task, args=(process_idx, )) p.start() processes.append(p) for p in processes: p.join() def test_viewer(self): params = {'episodeLengthSec': 1.0} e1 = MegaverseEnv('ObstaclesHard', 2, 2, 2, True, params) e1.reset() e1.render() for i in range(500): e1.step(sample_actions(e1)) e1.render() time.sleep(0.01) e1.close() ================================================ FILE: megaverse_rl/__init__.py ================================================ ================================================ FILE: megaverse_rl/enjoy_megaverse.py ================================================ import sys from sample_factory.enjoy import enjoy from megaverse_rl.train_megaverse import parse_megaverse_args, register_megaverse_components def main(): """Script entry point.""" register_megaverse_components() cfg = parse_megaverse_args(evaluation=True) status = enjoy(cfg) return status if __name__ == "__main__": sys.exit(main()) ================================================ FILE: megaverse_rl/megaverse_params.py ================================================ from sample_factory.utils.utils import str2bool def megaverse_override_defaults(env, parser): """RL params specific to Megaverse envs.""" parser.set_defaults( encoder_type="conv", encoder_subtype="convnet_simple", hidden_size=512, obs_subtract_mean=0.0, obs_scale=255.0, actor_worker_gpus=[0], # rollout workers need GPUs to render (provide a list of GPUs to use) env_gpu_actions=False, # but OpenAI Gym interface is entirely CPU-based env_gpu_observations=False, # (which can actually be optimized for even higher throughput) exploration_loss="symmetric_kl", exploration_loss_coeff=0.001, normalize_input=True, normalize_returns=True, with_vtrace=False, ) def add_megaverse_args(env, parser): p = parser p.add_argument( "--megaverse_num_envs_per_instance", default=1, type=int, help="Num simulated envs per instance of Megaverse" ) p.add_argument( "--megaverse_num_agents_per_env", default=4, type=int, help="Number of agents in a single env withing a Megaverse instance. Total number of agents in one Megaverse = num_envs_per_instance * num_agents_per_env", ) p.add_argument( "--megaverse_num_simulation_threads", default=1, type=int, help="Number of CPU threads to use per instance of Megaverse", ) p.add_argument("--megaverse_use_vulkan", default=True, type=str2bool, help="Whether to use Vulkan renderer") # Team Spirit options p.add_argument( "--megaverse_increase_team_spirit", default=False, type=str2bool, help="Increase team spirit from 0 to 1 over max_team_spirit_steps during training. At 1, the reward will be completely selfless.", ) p.add_argument( "--megaverse_max_team_spirit_steps", default=1e9, type=float, help="Number of training steps when team spirit will hit 1.", ) ================================================ FILE: megaverse_rl/megaverse_utils.py ================================================ from typing import Optional import gym from megaverse.megaverse_env import MegaverseEnv, make_env_multitask from sample_factory.envs.env_utils import RewardShapingInterface, TrainingInfoInterface from sample_factory.utils.utils import log class MegaverseSpec: def __init__(self, name): self.name = name MEGAVERSE_ENVS = [ MegaverseSpec("TowerBuilding"), MegaverseSpec("ObstaclesEasy"), MegaverseSpec("ObstaclesHard"), MegaverseSpec("Collect"), MegaverseSpec("Sokoban"), MegaverseSpec("HexMemory"), MegaverseSpec("HexExplore"), MegaverseSpec("Rearrange"), MegaverseSpec("multitask_Obstacles"), MegaverseSpec("multitask_megaverse8"), ] class Wrapper(gym.Wrapper, RewardShapingInterface, TrainingInfoInterface): """Sets interface for PBT reward shaping, and also extra summaries for multi-task learning.""" def __init__(self, env, increase_team_spirit, max_team_spirit_steps): gym.Wrapper.__init__(self, env) RewardShapingInterface.__init__(self) TrainingInfoInterface.__init__(self) self.num_agents = env.unwrapped.num_agents self.is_multiagent = env.unwrapped.is_multiagent self.episode_rewards = [0] * self.num_agents self.increase_team_spirit = increase_team_spirit self.max_team_spirit_steps = max_team_spirit_steps self.approx_total_training_steps = 0 def get_default_reward_shaping(self): return self.env.unwrapped.get_default_reward_shaping() def get_current_reward_shaping(self, agent_idx: int): return self.env.unwrapped.get_current_reward_shaping(agent_idx) def set_reward_shaping(self, reward_shaping: dict, agent_idx: int): return self.env.unwrapped.set_reward_shaping(reward_shaping, agent_idx) def reset(self, **kwargs): self.episode_rewards = [0] * self.num_agents return self.env.reset(), {} def step(self, action): obs, rewards, dones, infos = self.env.step(action) for i, info in enumerate(infos): self.episode_rewards[i] += rewards[i] if dones[i]: if "episode_extra_stats" not in info: info["episode_extra_stats"] = dict() extra_stats = info["episode_extra_stats"] info["true_objective"] = info["true_reward"] extra_stats[f"z_{self.env.unwrapped.scenario_name.casefold()}_true_objective"] = info["true_reward"] extra_stats[f"z_{self.env.unwrapped.scenario_name.casefold()}_reward"] = self.episode_rewards[i] approx_total_training_steps = self.training_info.get("approx_total_training_steps", 0) extra_stats["z_approx_total_training_steps"] = approx_total_training_steps self.episode_rewards[i] = 0 if self.increase_team_spirit: rew_shaping = self.get_current_reward_shaping(i) rew_shaping["teamSpirit"] = min(approx_total_training_steps / self.max_team_spirit_steps, 1.0) self.set_reward_shaping(rew_shaping, i) extra_stats["teamSpirit"] = rew_shaping["teamSpirit"] terminated = dones truncated = [False] * len(dones) return obs, rewards, terminated, truncated, infos def make_megaverse(env_name, cfg=None, env_config=None, render_mode: Optional[str] = None, **kwargs): scenario_name = env_name.casefold() log.debug("Using scenario %s", scenario_name) if "multitask" in scenario_name: if env_config is not None and "worker_index" in env_config: task_idx = env_config["worker_index"] else: log.warning( "Could not find information about task id. Use task_id=0. (It is okay if this message appears once)" ) task_idx = 0 env = make_env_multitask( scenario_name, task_idx, num_envs=cfg.megaverse_num_envs_per_instance, num_agents_per_env=cfg.megaverse_num_agents_per_env, num_simulation_threads=cfg.megaverse_num_simulation_threads, use_vulkan=cfg.megaverse_use_vulkan, ) else: env = MegaverseEnv( scenario_name=scenario_name, num_envs=cfg.megaverse_num_envs_per_instance, num_agents_per_env=cfg.megaverse_num_agents_per_env, num_simulation_threads=cfg.megaverse_num_simulation_threads, use_vulkan=cfg.megaverse_use_vulkan, ) env = Wrapper(env, cfg.megaverse_increase_team_spirit, cfg.megaverse_max_team_spirit_steps) return env ================================================ FILE: megaverse_rl/runs/__init__.py ================================================ ================================================ FILE: megaverse_rl/runs/megaverse_base_experiments.py ================================================ from sample_factory.launcher.run_description import Experiment, ParamGrid _params = ParamGrid([ ('env', ['TowerBuilding', 'ObstaclesEasy', 'ObstaclesHard', 'Collect', 'Sokoban', 'HexMemory', 'HexExplore', 'Rearrange']), ('seed', [11111, 22222, 33333, 44444, 55555]), ]) _cli = 'python -m megaverse_rl.train_megaverse --train_for_seconds=360000000 --train_for_env_steps=2000000000 --algo=APPO --gamma=0.997 --use_rnn=True --rnn_num_layers=2 --num_workers=1 --num_envs_per_worker=2 --num_epochs=1 --rollout=32 --recurrence=32 --batch_size=2048 --actor_worker_gpus 0 --num_policies=1 --with_pbt=False --max_grad_norm=0.0 --exploration_loss=symmetric_kl --exploration_loss_coeff=0.001 --megaverse_num_simulation_threads=1 --megaverse_use_vulkan=True --policy_workers_per_policy=2 --reward_clip=30' EXPERIMENT_1AGENT = Experiment( 'megaverse_1ag', _cli + ' --megaverse_num_envs_per_instance=36 --megaverse_num_agents_per_env=1', _params.generate_params(randomize=False), ) EXPERIMENT_2AGENTS = Experiment( 'megaverse_2ag', _cli + ' --megaverse_num_envs_per_instance=18 --megaverse_num_agents_per_env=2', _params.generate_params(randomize=False), ) EXPERIMENT_4AGENTS = Experiment( 'megaverse_4ag', _cli + ' --megaverse_num_envs_per_instance=9 --megaverse_num_agents_per_env=4', _params.generate_params(randomize=False), ) ================================================ FILE: megaverse_rl/runs/multi_agent.py ================================================ from sample_factory.launcher.run_description import RunDescription from megaverse_rl.runs.megaverse_base_experiments import EXPERIMENT_4AGENTS, EXPERIMENT_2AGENTS RUN_DESCRIPTION = RunDescription('megaverse_v115_multi_agent_v55', experiments=[EXPERIMENT_2AGENTS, EXPERIMENT_4AGENTS]) ================================================ FILE: megaverse_rl/runs/multitask.py ================================================ from sample_factory.launcher.run_description import RunDescription, Experiment, ParamGrid _params = ParamGrid([ ('env', ['multitask_megaverse8']), ('seed', [11111, 22222, 33333, 44444, 55555]), ]) _cli = 'python -m megaverse_rl.train_megaverse --train_for_seconds=360000000 --train_for_env_steps=2000000000 --algo=APPO --gamma=0.997 --use_rnn=True --rnn_num_layers=2 --num_workers=12 --num_envs_per_worker=2 --num_epochs=1 --rollout=32 --recurrence=32 --batch_size=2048 --actor_worker_gpus 0 --num_policies=1 --with_pbt=False --max_grad_norm=0.0 --exploration_loss=symmetric_kl --exploration_loss_coeff=0.001 --megaverse_num_simulation_threads=1 --megaverse_use_vulkan=True --policy_workers_per_policy=2 --reward_clip=30 --pbt_mix_policies_in_one_env=False' EXPERIMENT_1AGENT = Experiment( 'megaverse_multitask_obs', _cli + ' --megaverse_num_envs_per_instance=36 --megaverse_num_agents_per_env=1', _params.generate_params(randomize=False), ) RUN_DESCRIPTION = RunDescription('megaverse_v115_multitask8_v55', experiments=[EXPERIMENT_1AGENT]) ================================================ FILE: megaverse_rl/runs/multitask_obstacles.py ================================================ from sample_factory.launcher.run_description import RunDescription, Experiment, ParamGrid _params = ParamGrid([ ('env', ['multitask_Obstacles']), ('seed', [11111, 22222, 33333, 44444, 55555]), ]) _cli = 'python -m megaverse_rl.train_megaverse --train_for_seconds=360000000 --train_for_env_steps=10000000000 --algo=APPO --gamma=0.997 --use_rnn=True --rnn_num_layers=2 --num_workers=12 --num_envs_per_worker=2 --num_epochs=1 --rollout=32 --recurrence=32 --batch_size=2048 --actor_worker_gpus 0 --num_policies=1 --with_pbt=False --max_grad_norm=0.0 --exploration_loss=symmetric_kl --exploration_loss_coeff=0.001 --megaverse_num_simulation_threads=1 --megaverse_use_vulkan=True --policy_workers_per_policy=2 --learner_main_loop_num_cores=2 --reward_clip=30 --pbt_mix_policies_in_one_env=False' EXPERIMENT_1AGENT = Experiment( 'megaverse_multitask_obs', _cli + ' --megaverse_num_envs_per_instance=36 --megaverse_num_agents_per_env=1', _params.generate_params(randomize=False), ) RUN_DESCRIPTION = RunDescription('megaverse_v115_multitask_obstacles_v55', experiments=[EXPERIMENT_1AGENT]) ================================================ FILE: megaverse_rl/runs/performance_benchmark.py ================================================ from sample_factory.launcher.run_description import RunDescription, Experiment, ParamGrid NUM_WORKERS = 20 # typically num logical cores NUM_WORKERS_MEGAVERSE = 10 # typically num logical cores / 2, limited by the num of available Vulkan contexts TIMEOUT_SECONDS = 180 SAMPLER_GPUS = '0' # replace with '0 1 2 3 4 5 6 7' for 8-GPU server _basic_cli = f'python -m sample_factory.run_algorithm --algo=DUMMY_SAMPLER --num_workers={NUM_WORKERS} --num_envs_per_worker=1 --experiment=benchmark --timeout_seconds={TIMEOUT_SECONDS}' _params_basic_envs = ParamGrid([ ('env', ['doom_benchmark', 'atari_breakout', 'dmlab_benchmark']), ]) _experiment_basic_envs = Experiment( 'benchmark_basic_envs', _basic_cli, _params_basic_envs.generate_params(randomize=False), ) _megaverse_cli = f'python -m sample_factory.run_algorithm --algo=DUMMY_SAMPLER --num_workers={NUM_WORKERS_MEGAVERSE} --num_envs_per_worker=1 --experiment=benchmark --sampler_worker_gpus {SAMPLER_GPUS} --megaverse_num_envs_per_instance=64 --megaverse_num_agents_per_env=2 --megaverse_num_simulation_threads=2 --timeout_seconds={TIMEOUT_SECONDS}' _params_megaverse = ParamGrid([ ('env', ['obstacleshard']), ('megaverse_use_vulkan', [True, False]), ]) _experiment_megaverse = Experiment( 'benchmark_megaverse', _megaverse_cli, _params_megaverse.generate_params(randomize=False), ) RUN_DESCRIPTION = RunDescription('megaverse_bench_sampling', experiments=[_experiment_basic_envs, _experiment_megaverse]) ================================================ FILE: megaverse_rl/runs/performance_benchmark_all_envs.py ================================================ from sample_factory.launcher.run_description import RunDescription, Experiment, ParamGrid NUM_WORKERS_MEGAVERSE = 48 # typically num logical cores / 2, limited by the num of available Vulkan contexts TIMEOUT_SECONDS = 180 SAMPLER_GPUS = '0 1 2 3 4 5 6 7' # replace with '0 1 2 3 4 5 6 7' for 8-GPU server _megaverse_cli = f'python -m sample_factory.run_algorithm --algo=DUMMY_SAMPLER --num_workers={NUM_WORKERS_MEGAVERSE} --num_envs_per_worker=1 --experiment=benchmark --sampler_worker_gpus {SAMPLER_GPUS} --megaverse_num_envs_per_instance=64 --megaverse_num_agents_per_env=2 --megaverse_num_simulation_threads=2 --timeout_seconds={TIMEOUT_SECONDS}' _params_megaverse = ParamGrid([ ('env', ['TowerBuilding', 'ObstaclesEasy', 'ObstaclesHard', 'Collect', 'Sokoban', 'HexMemory', 'HexExplore', 'Rearrange']), ('megaverse_use_vulkan', [True]), ]) _experiment_megaverse = Experiment( 'benchmark_megaverse_8', _megaverse_cli, _params_megaverse.generate_params(randomize=False), ) RUN_DESCRIPTION = RunDescription('megaverse_bench_sampling_all_envs', experiments=[_experiment_megaverse]) ================================================ FILE: megaverse_rl/runs/single_agent.py ================================================ from sample_factory.launcher.run_description import RunDescription from megaverse_rl.runs.megaverse_base_experiments import EXPERIMENT_1AGENT RUN_DESCRIPTION = RunDescription('megaverse_arxiv', experiments=[EXPERIMENT_1AGENT]) ================================================ FILE: megaverse_rl/runs/training_benchmark.py ================================================ from sample_factory.launcher.run_description import RunDescription, Experiment, ParamGrid NUM_WORKERS = 20 # typically num logical cores NUM_WORKERS_MEGAVERSE = 10 # typically num logical cores / 2, limited by the num of available Vulkan contexts TIMEOUT_SECONDS = 180 ACTOR_GPUS = '0' # replace with '0 1 2 3 4 5 6 7' for 8-GPU server NUM_POLICIES = 1 _basic_cli = f'python -m megaverse_rl.train_megaverse --train_for_seconds={TIMEOUT_SECONDS} --train_for_env_steps=20000000000 --algo=APPO --gamma=0.997 --use_rnn=True --rnn_num_layers=2 --num_workers={NUM_WORKERS} --num_envs_per_worker=16 --num_epochs=1 --rollout=32 --recurrence=32 --batch_size=2048 --num_policies={NUM_POLICIES} --with_pbt=False --max_grad_norm=0.0 --exploration_loss=symmetric_kl --exploration_loss_coeff=0.001 --policy_workers_per_policy=2 --learner_main_loop_num_cores=4 --reward_clip=30' _params_basic_envs = ParamGrid([ ('env', ['doom_benchmark', 'atari_breakout', 'dmlab_benchmark']), ]) _experiment_basic_envs = Experiment( 'benchmark_basic_envs', _basic_cli, _params_basic_envs.generate_params(randomize=False), ) _megaverse_cli = f'python -m megaverse_rl.train_megaverse --train_for_seconds={TIMEOUT_SECONDS} --train_for_env_steps=20000000000 --algo=APPO --gamma=0.997 --use_rnn=True --rnn_num_layers=2 --num_workers={NUM_WORKERS_MEGAVERSE} --num_envs_per_worker=2 --num_epochs=1 --rollout=32 --recurrence=32 --batch_size=2048 --actor_worker_gpus {ACTOR_GPUS} --num_policies={NUM_POLICIES} --with_pbt=False --max_grad_norm=0.0 --exploration_loss=symmetric_kl --exploration_loss_coeff=0.001 --megaverse_num_simulation_threads=2 --megaverse_use_vulkan=True --policy_workers_per_policy=2 --learner_main_loop_num_cores=4 --reward_clip=30 --megaverse_num_envs_per_instance=36 --megaverse_num_agents_per_env=1 --pbt_mix_policies_in_one_env=False' _params_megaverse = ParamGrid([ ('env', ['megaverse_obstacleshard']), ('megaverse_use_vulkan', [True, False]), ]) _experiment_megaverse = Experiment( 'benchmark_megaverse', _megaverse_cli, _params_megaverse.generate_params(randomize=False), ) RUN_DESCRIPTION = RunDescription('megaverse_train_benchmark', experiments=[_experiment_basic_envs, _experiment_megaverse]) ================================================ FILE: megaverse_rl/sampling_benchmark.py ================================================ """ Measure pure sampling throughput. """ import sys from sample_factory.algorithms.utils.arguments import arg_parser, postprocess_args from sample_factory.run_algorithm import run_algorithm from sample_factory.utils.get_available_gpus import get_gpus_without_triggering_pytorch_cuda_initialization from sample_factory.utils.utils import log from megaverse_rl.megaverse_utils import register_env def main(): """Script entry point.""" gpus = get_gpus_without_triggering_pytorch_cuda_initialization() gpus = gpus.strip().split(',') gpus = [int(g) for g in gpus] if len(gpus) <= 0: log.error('Sampling benchmark requires at least one GPU') else: log.debug('Have %d GPUs (%r)', len(gpus), gpus) register_env() argv = sys.argv[1:] argv.append('--algo=DUMMY_SAMPLER') parser = arg_parser(argv) parser.set_defaults( sampler_worker_gpus=gpus, num_workers=len(gpus) * 10, megaverse_num_envs_per_instance=32, megaverse_num_agents_per_env=4, megaverse_num_simulation_threads=2, ) # parse all the arguments (algo, env, and optionally evaluation) cfg = parser.parse_args(argv) cfg = postprocess_args(cfg, argv, parser) status = run_algorithm(cfg) return status if __name__ == '__main__': sys.exit(main()) ================================================ FILE: megaverse_rl/slurm/sbatch_template.sh ================================================ #!/bin/bash source /homes/petrenko/miniconda3/etc/profile.d/conda.sh conda activate megaverse cd ~/megaverse ================================================ FILE: megaverse_rl/slurm/slurm_cli.txt ================================================ python -m sample_factory.launcher.run --run=megaverse_rl.runs.single_agent --runner=slurm --slurm_workdir=./megaverse_single_agent --experiment_suffix=slurm --pause_between=1 --slurm_gpus_per_job=1 --slurm_cpus_per_gpu=12 --slurm_sbatch_template=./megaverse_rl/slurm/sbatch_template.sh --slurm_print_only=False ================================================ FILE: megaverse_rl/tests/__init__.py ================================================ ================================================ FILE: megaverse_rl/tests/test_megaverse_env.py ================================================ from unittest import TestCase from sample_factory.envs.create_env import create_env from sample_factory.utils.utils import log from megaverse_rl.train_megaverse import register_megaverse_components, parse_megaverse_args class TestMegaverse(TestCase): def test_megaverse(self): register_megaverse_components() cfg = parse_megaverse_args(['--algo=APPO', '--env=Sokoban', '--experiment=test_megaverse']) env = create_env(cfg.env, cfg=cfg) log.info('Env action space: %r', env.action_space) log.info('Env obs space: %r', env.observation_space) env.reset() total_rew = 0 for i in range(1000): obs, rew, terminated, truncated, info = env.step([env.action_space.sample() for _ in range(env.num_agents)]) total_rew += sum(rew) log.info('Total rew: %.3f', total_rew) ================================================ FILE: megaverse_rl/train_megaverse.py ================================================ """ Main script for training agents with SampleFactory. """ import sys from sample_factory.cfg.arguments import parse_full_cfg, parse_sf_args from sample_factory.envs.env_utils import register_env from sample_factory.train import run_rl from megaverse_rl.megaverse_params import add_megaverse_args, megaverse_override_defaults from megaverse_rl.megaverse_utils import MEGAVERSE_ENVS, make_megaverse def register_megaverse_envs(): for env in MEGAVERSE_ENVS: register_env(env.name, make_megaverse) def register_megaverse_components(): register_megaverse_envs() def parse_megaverse_args(argv=None, evaluation=False): parser, partial_cfg = parse_sf_args(argv=argv, evaluation=evaluation) add_megaverse_args(partial_cfg.env, parser) megaverse_override_defaults(partial_cfg.env, parser) final_cfg = parse_full_cfg(parser, argv) return final_cfg def main(): """Script entry point.""" register_megaverse_components() cfg = parse_megaverse_args() status = run_rl(cfg) return status if __name__ == "__main__": sys.exit(main()) ================================================ FILE: setup.cfg ================================================ [build_ext] build_temp=build ================================================ FILE: setup.py ================================================ import os import sys import multiprocessing import subprocess from os.path import join as pjoin from setuptools import setup, Extension, find_packages from setuptools.command.build_ext import build_ext supported_platforms = ["Linux", "Mac OS-X"] def find_in_path(name, path): """Find a file in a search path""" # Adapted fom http://code.activestate.com/recipes/52224 for dir in path.split(os.pathsep): binpath = pjoin(dir, name) if os.path.exists(binpath): return os.path.abspath(binpath) return None def locate_cuda(): """Locate the CUDA environment on the system Starts by looking for the CUDAHOME env variable. If not found, everything is based on finding 'nvcc' in the PATH. """ # First check if the CUDAHOME env variable is in use if 'CUDAHOME' in os.environ: home = os.environ['CUDAHOME'] nvcc = pjoin(home, 'bin', 'nvcc') else: # Otherwise, search the PATH for NVCC nvcc = find_in_path('nvcc', os.environ['PATH']) if nvcc is None: raise EnvironmentError('The nvcc binary could not be ' 'located in your $PATH. Either add it to your path, ' 'or set $CUDAHOME') return nvcc class CMakeExtension(Extension): def __init__(self, name, sourcedir=''): Extension.__init__(self, name, sources=[]) self.sourcedir = os.path.abspath(sourcedir) class CMakeBuild(build_ext): def run(self): try: subprocess.check_output(['cmake', '--version']) except OSError: raise RuntimeError( 'CMake must be installed to build the following extensions: ' + ', '.join(e.name for e in self.extensions), ) for ext in self.extensions: self.build_extension(ext) def build_extension(self, ext): extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) # required for auto-detection of auxiliary "native" libs if not extdir.endswith(os.path.sep): extdir += os.path.sep cmake_args = [ f'-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}', f'-DPYTHON_EXECUTABLE={sys.executable}', # f'-DOpenCV_DIR =/home/alex/all/lib/opencv/build', # TODO!!! ] if sys.platform != "darwin": # if not on mac, look for cuda/nvcc cmake_args.append(f'-DCMAKE_CUDA_COMPILER={locate_cuda()}') # that's a hacky way to do it but the best idea I have at the moment bullet_root = os.environ.get('BULLET_ROOT', None) if bullet_root is not None: cmake_args.append(f'-DBULLET_ROOT={bullet_root}') cfg = 'Debug' if self.debug else 'Release' build_args = ['--config', cfg] if os.environ.get('MEGAVERSE_WITH_GUI') == '1': build_gui = 'ON' else: build_gui = 'OFF' cmake_args += [f'-DCMAKE_BUILD_TYPE={cfg}', f'-DBUILD_GUI_APPS={build_gui}'] build_args += ['--', f'-j{multiprocessing.cpu_count()}'] env = os.environ.copy() if not os.path.exists(self.build_temp): os.makedirs(self.build_temp) print('Build temp directory is ', self.build_temp) cmake_cmd = ['cmake', ext.sourcedir] + cmake_args print(f'CMake command is: {" ".join(cmake_cmd)}') subprocess.check_call(cmake_cmd, cwd=self.build_temp, env=env) subprocess.check_call( ['cmake', '--build', '.', '--target', 'megaverse'] + build_args, cwd=self.build_temp, ) print('Completed the build!') def main(): setup( name='megaverse', version='0.0.2', author='Aleksei Petrenko', author_email='apetrenko1991@gmail.com', description='Fast immersive environment', long_description='', platforms=supported_platforms, packages=find_packages(exclude=['test', 'benchmarks']), include_package_data=True, ext_modules=[CMakeExtension('megaverse.extension.megaverse', 'src')], cmdclass=dict(build_ext=CMakeBuild), zip_safe=False, install_requires=[ 'gym>=0.17.1', ], ) return 0 if __name__ == '__main__': sys.exit(main()) ================================================ FILE: src/3rdparty/CMakeLists.txt ================================================ if (NOT APPLE) add_subdirectory(v4r EXCLUDE_FROM_ALL) endif () add_subdirectory(corrade EXCLUDE_FROM_ALL) find_package(Corrade REQUIRED Main) if (BUILD_GUI_APPS) set(WITH_SDL2APPLICATION ON CACHE BOOL "" FORCE) else() message(STATUS "Build without GUI apps!") endif() if (NOT CORRADE_TARGET_APPLE) set(WITH_WINDOWLESSEGLAPPLICATION ON CACHE BOOL "" FORCE) else () set(WITH_WINDOWLESSCGLAPPLICATION ON CACHE BOOL "WITH_WINDOWLESSCGLAPPLICATION" FORCE) endif () set(WITH_TGAIMPORTER ON CACHE BOOL "" FORCE) add_subdirectory(magnum EXCLUDE_FROM_ALL) #find_package(Bullet REQUIRED) set(WITH_BULLET ON CACHE BOOL "" FORCE) add_subdirectory(magnum-integration EXCLUDE_FROM_ALL) find_package(Magnum REQUIRED GL MeshTools Primitives SceneGraph Shaders Trade) if (BUILD_GUI_APPS) find_package(Magnum REQUIRED Sdl2Application) endif() find_package(MagnumIntegration REQUIRED Bullet) add_subdirectory(googletest-1.10.0) include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) if (NOT CORRADE_TARGET_APPLE) add_subdirectory(glad) endif () add_subdirectory(pybind11) ================================================ FILE: src/3rdparty/glad/CMakeLists.txt ================================================ add_library_default(glad) ================================================ FILE: src/3rdparty/glad/include/KHR/khrplatform.h ================================================ #ifndef __khrplatform_h_ #define __khrplatform_h_ /* ** Copyright (c) 2008-2018 The Khronos Group Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and/or associated documentation files (the ** "Materials"), to deal in the Materials without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Materials, and to ** permit persons to whom the Materials are furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be included ** in all copies or substantial portions of the Materials. ** ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. */ /* Khronos platform-specific types and definitions. * * The master copy of khrplatform.h is maintained in the Khronos EGL * Registry repository at https://github.com/KhronosGroup/EGL-Registry * The last semantic modification to khrplatform.h was at commit ID: * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 * * Adopters may modify this file to suit their platform. Adopters are * encouraged to submit platform specific modifications to the Khronos * group so that they can be included in future versions of this file. * Please submit changes by filing pull requests or issues on * the EGL Registry repository linked above. * * * See the Implementer's Guidelines for information about where this file * should be located on your system and for more details of its use: * http://www.khronos.org/registry/implementers_guide.pdf * * This file should be included as * #include * by Khronos client API header files that use its types and defines. * * The types in khrplatform.h should only be used to define API-specific types. * * Types defined in khrplatform.h: * khronos_int8_t signed 8 bit * khronos_uint8_t unsigned 8 bit * khronos_int16_t signed 16 bit * khronos_uint16_t unsigned 16 bit * khronos_int32_t signed 32 bit * khronos_uint32_t unsigned 32 bit * khronos_int64_t signed 64 bit * khronos_uint64_t unsigned 64 bit * khronos_intptr_t signed same number of bits as a pointer * khronos_uintptr_t unsigned same number of bits as a pointer * khronos_ssize_t signed size * khronos_usize_t unsigned size * khronos_float_t signed 32 bit floating point * khronos_time_ns_t unsigned 64 bit time in nanoseconds * khronos_utime_nanoseconds_t unsigned time interval or absolute time in * nanoseconds * khronos_stime_nanoseconds_t signed time interval in nanoseconds * khronos_boolean_enum_t enumerated boolean type. This should * only be used as a base type when a client API's boolean type is * an enum. Client APIs which use an integer or other type for * booleans cannot use this as the base type for their boolean. * * Tokens defined in khrplatform.h: * * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. * * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. * * Calling convention macros defined in this file: * KHRONOS_APICALL * KHRONOS_APIENTRY * KHRONOS_APIATTRIBUTES * * These may be used in function prototypes as: * * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( * int arg1, * int arg2) KHRONOS_APIATTRIBUTES; */ /*------------------------------------------------------------------------- * Definition of KHRONOS_APICALL *------------------------------------------------------------------------- * This precedes the return type of the function in the function prototype. */ #if defined(_WIN32) && !defined(__SCITECH_SNAP__) # define KHRONOS_APICALL __declspec(dllimport) #elif defined (__SYMBIAN32__) # define KHRONOS_APICALL IMPORT_C #elif defined(__ANDROID__) # define KHRONOS_APICALL __attribute__((visibility("default"))) #else # define KHRONOS_APICALL #endif /*------------------------------------------------------------------------- * Definition of KHRONOS_APIENTRY *------------------------------------------------------------------------- * This follows the return type of the function and precedes the function * name in the function prototype. */ #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) /* Win32 but not WinCE */ # define KHRONOS_APIENTRY __stdcall #else # define KHRONOS_APIENTRY #endif /*------------------------------------------------------------------------- * Definition of KHRONOS_APIATTRIBUTES *------------------------------------------------------------------------- * This follows the closing parenthesis of the function prototype arguments. */ #if defined (__ARMCC_2__) #define KHRONOS_APIATTRIBUTES __softfp #else #define KHRONOS_APIATTRIBUTES #endif /*------------------------------------------------------------------------- * basic type definitions *-----------------------------------------------------------------------*/ #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) /* * Using */ #include typedef int32_t khronos_int32_t; typedef uint32_t khronos_uint32_t; typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif defined(__VMS ) || defined(__sgi) /* * Using */ #include typedef int32_t khronos_int32_t; typedef uint32_t khronos_uint32_t; typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) /* * Win32 */ typedef __int32 khronos_int32_t; typedef unsigned __int32 khronos_uint32_t; typedef __int64 khronos_int64_t; typedef unsigned __int64 khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif defined(__sun__) || defined(__digital__) /* * Sun or Digital */ typedef int khronos_int32_t; typedef unsigned int khronos_uint32_t; #if defined(__arch64__) || defined(_LP64) typedef long int khronos_int64_t; typedef unsigned long int khronos_uint64_t; #else typedef long long int khronos_int64_t; typedef unsigned long long int khronos_uint64_t; #endif /* __arch64__ */ #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif 0 /* * Hypothetical platform with no float or int64 support */ typedef int khronos_int32_t; typedef unsigned int khronos_uint32_t; #define KHRONOS_SUPPORT_INT64 0 #define KHRONOS_SUPPORT_FLOAT 0 #else /* * Generic fallback */ #include typedef int32_t khronos_int32_t; typedef uint32_t khronos_uint32_t; typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #endif /* * Types that are (so far) the same on all platforms */ typedef signed char khronos_int8_t; typedef unsigned char khronos_uint8_t; typedef signed short int khronos_int16_t; typedef unsigned short int khronos_uint16_t; /* * Types that differ between LLP64 and LP64 architectures - in LLP64, * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears * to be the only LLP64 architecture in current use. */ #ifdef _WIN64 typedef signed long long int khronos_intptr_t; typedef unsigned long long int khronos_uintptr_t; typedef signed long long int khronos_ssize_t; typedef unsigned long long int khronos_usize_t; #else typedef signed long int khronos_intptr_t; typedef unsigned long int khronos_uintptr_t; typedef signed long int khronos_ssize_t; typedef unsigned long int khronos_usize_t; #endif #if KHRONOS_SUPPORT_FLOAT /* * Float type */ typedef float khronos_float_t; #endif #if KHRONOS_SUPPORT_INT64 /* Time types * * These types can be used to represent a time interval in nanoseconds or * an absolute Unadjusted System Time. Unadjusted System Time is the number * of nanoseconds since some arbitrary system event (e.g. since the last * time the system booted). The Unadjusted System Time is an unsigned * 64 bit value that wraps back to 0 every 584 years. Time intervals * may be either signed or unsigned. */ typedef khronos_uint64_t khronos_utime_nanoseconds_t; typedef khronos_int64_t khronos_stime_nanoseconds_t; #endif /* * Dummy value used to pad enum types to 32 bits. */ #ifndef KHRONOS_MAX_ENUM #define KHRONOS_MAX_ENUM 0x7FFFFFFF #endif /* * Enumerated boolean type * * Values other than zero should be considered to be true. Therefore * comparisons should not be made against KHRONOS_TRUE. */ typedef enum { KHRONOS_FALSE = 0, KHRONOS_TRUE = 1, KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM } khronos_boolean_enum_t; #endif /* __khrplatform_h_ */ ================================================ FILE: src/3rdparty/glad/include/glad/glad_egl.h ================================================ /* EGL loader generated by glad 0.1.25 on Mon Jan 14 16:56:21 2019. Language/Generator: C/C++ Specification: egl APIs: egl=1.5 Profile: - Extensions: EGL_ANDROID_blob_cache, EGL_ANDROID_create_native_client_buffer, EGL_ANDROID_framebuffer_target, EGL_ANDROID_front_buffer_auto_refresh, EGL_ANDROID_get_frame_timestamps, EGL_ANDROID_get_native_client_buffer, EGL_ANDROID_image_native_buffer, EGL_ANDROID_native_fence_sync, EGL_ANDROID_presentation_time, EGL_ANDROID_recordable, EGL_ANGLE_d3d_share_handle_client_buffer, EGL_ANGLE_device_d3d, EGL_ANGLE_query_surface_pointer, EGL_ANGLE_surface_d3d_texture_2d_share_handle, EGL_ANGLE_window_fixed_size, EGL_ARM_implicit_external_sync, EGL_ARM_pixmap_multisample_discard, EGL_EXT_bind_to_front, EGL_EXT_buffer_age, EGL_EXT_client_extensions, EGL_EXT_client_sync, EGL_EXT_compositor, EGL_EXT_create_context_robustness, EGL_EXT_device_base, EGL_EXT_device_drm, EGL_EXT_device_enumeration, EGL_EXT_device_openwf, EGL_EXT_device_query, EGL_EXT_gl_colorspace_bt2020_linear, EGL_EXT_gl_colorspace_bt2020_pq, EGL_EXT_gl_colorspace_display_p3, EGL_EXT_gl_colorspace_display_p3_linear, EGL_EXT_gl_colorspace_display_p3_passthrough, EGL_EXT_gl_colorspace_scrgb, EGL_EXT_gl_colorspace_scrgb_linear, EGL_EXT_image_dma_buf_import, EGL_EXT_image_dma_buf_import_modifiers, EGL_EXT_image_gl_colorspace, EGL_EXT_image_implicit_sync_control, EGL_EXT_multiview_window, EGL_EXT_output_base, EGL_EXT_output_drm, EGL_EXT_output_openwf, EGL_EXT_pixel_format_float, EGL_EXT_platform_base, EGL_EXT_platform_device, EGL_EXT_platform_wayland, EGL_EXT_platform_x11, EGL_EXT_protected_content, EGL_EXT_protected_surface, EGL_EXT_stream_consumer_egloutput, EGL_EXT_surface_CTA861_3_metadata, EGL_EXT_surface_SMPTE2086_metadata, EGL_EXT_swap_buffers_with_damage, EGL_EXT_sync_reuse, EGL_EXT_yuv_surface, EGL_HI_clientpixmap, EGL_HI_colorformats, EGL_IMG_context_priority, EGL_IMG_image_plane_attribs, EGL_KHR_cl_event, EGL_KHR_cl_event2, EGL_KHR_client_get_all_proc_addresses, EGL_KHR_config_attribs, EGL_KHR_context_flush_control, EGL_KHR_create_context, EGL_KHR_create_context_no_error, EGL_KHR_debug, EGL_KHR_display_reference, EGL_KHR_fence_sync, EGL_KHR_get_all_proc_addresses, EGL_KHR_gl_colorspace, EGL_KHR_gl_renderbuffer_image, EGL_KHR_gl_texture_2D_image, EGL_KHR_gl_texture_3D_image, EGL_KHR_gl_texture_cubemap_image, EGL_KHR_image, EGL_KHR_image_base, EGL_KHR_image_pixmap, EGL_KHR_lock_surface, EGL_KHR_lock_surface2, EGL_KHR_lock_surface3, EGL_KHR_mutable_render_buffer, EGL_KHR_no_config_context, EGL_KHR_partial_update, EGL_KHR_platform_android, EGL_KHR_platform_gbm, EGL_KHR_platform_wayland, EGL_KHR_platform_x11, EGL_KHR_reusable_sync, EGL_KHR_stream, EGL_KHR_stream_attrib, EGL_KHR_stream_consumer_gltexture, EGL_KHR_stream_cross_process_fd, EGL_KHR_stream_fifo, EGL_KHR_stream_producer_aldatalocator, EGL_KHR_stream_producer_eglsurface, EGL_KHR_surfaceless_context, EGL_KHR_swap_buffers_with_damage, EGL_KHR_vg_parent_image, EGL_KHR_wait_sync, EGL_MESA_drm_image, EGL_MESA_image_dma_buf_export, EGL_MESA_platform_gbm, EGL_MESA_platform_surfaceless, EGL_NOK_swap_region, EGL_NOK_swap_region2, EGL_NOK_texture_from_pixmap, EGL_NV_3dvision_surface, EGL_NV_context_priority_realtime, EGL_NV_coverage_sample, EGL_NV_coverage_sample_resolve, EGL_NV_cuda_event, EGL_NV_depth_nonlinear, EGL_NV_device_cuda, EGL_NV_native_query, EGL_NV_post_convert_rounding, EGL_NV_post_sub_buffer, EGL_NV_robustness_video_memory_purge, EGL_NV_stream_consumer_gltexture_yuv, EGL_NV_stream_cross_display, EGL_NV_stream_cross_object, EGL_NV_stream_cross_partition, EGL_NV_stream_cross_process, EGL_NV_stream_cross_system, EGL_NV_stream_fifo_next, EGL_NV_stream_fifo_synchronous, EGL_NV_stream_flush, EGL_NV_stream_frame_limits, EGL_NV_stream_metadata, EGL_NV_stream_remote, EGL_NV_stream_reset, EGL_NV_stream_socket, EGL_NV_stream_socket_inet, EGL_NV_stream_socket_unix, EGL_NV_stream_sync, EGL_NV_sync, EGL_NV_system_time, EGL_TIZEN_image_native_buffer, EGL_TIZEN_image_native_surface Loader: True Local files: False Omit khrplatform: False Commandline: --api="egl=1.5" --generator="c" --spec="egl" --extensions="EGL_ANDROID_blob_cache,EGL_ANDROID_create_native_client_buffer,EGL_ANDROID_framebuffer_target,EGL_ANDROID_front_buffer_auto_refresh,EGL_ANDROID_get_frame_timestamps,EGL_ANDROID_get_native_client_buffer,EGL_ANDROID_image_native_buffer,EGL_ANDROID_native_fence_sync,EGL_ANDROID_presentation_time,EGL_ANDROID_recordable,EGL_ANGLE_d3d_share_handle_client_buffer,EGL_ANGLE_device_d3d,EGL_ANGLE_query_surface_pointer,EGL_ANGLE_surface_d3d_texture_2d_share_handle,EGL_ANGLE_window_fixed_size,EGL_ARM_implicit_external_sync,EGL_ARM_pixmap_multisample_discard,EGL_EXT_bind_to_front,EGL_EXT_buffer_age,EGL_EXT_client_extensions,EGL_EXT_client_sync,EGL_EXT_compositor,EGL_EXT_create_context_robustness,EGL_EXT_device_base,EGL_EXT_device_drm,EGL_EXT_device_enumeration,EGL_EXT_device_openwf,EGL_EXT_device_query,EGL_EXT_gl_colorspace_bt2020_linear,EGL_EXT_gl_colorspace_bt2020_pq,EGL_EXT_gl_colorspace_display_p3,EGL_EXT_gl_colorspace_display_p3_linear,EGL_EXT_gl_colorspace_display_p3_passthrough,EGL_EXT_gl_colorspace_scrgb,EGL_EXT_gl_colorspace_scrgb_linear,EGL_EXT_image_dma_buf_import,EGL_EXT_image_dma_buf_import_modifiers,EGL_EXT_image_gl_colorspace,EGL_EXT_image_implicit_sync_control,EGL_EXT_multiview_window,EGL_EXT_output_base,EGL_EXT_output_drm,EGL_EXT_output_openwf,EGL_EXT_pixel_format_float,EGL_EXT_platform_base,EGL_EXT_platform_device,EGL_EXT_platform_wayland,EGL_EXT_platform_x11,EGL_EXT_protected_content,EGL_EXT_protected_surface,EGL_EXT_stream_consumer_egloutput,EGL_EXT_surface_CTA861_3_metadata,EGL_EXT_surface_SMPTE2086_metadata,EGL_EXT_swap_buffers_with_damage,EGL_EXT_sync_reuse,EGL_EXT_yuv_surface,EGL_HI_clientpixmap,EGL_HI_colorformats,EGL_IMG_context_priority,EGL_IMG_image_plane_attribs,EGL_KHR_cl_event,EGL_KHR_cl_event2,EGL_KHR_client_get_all_proc_addresses,EGL_KHR_config_attribs,EGL_KHR_context_flush_control,EGL_KHR_create_context,EGL_KHR_create_context_no_error,EGL_KHR_debug,EGL_KHR_display_reference,EGL_KHR_fence_sync,EGL_KHR_get_all_proc_addresses,EGL_KHR_gl_colorspace,EGL_KHR_gl_renderbuffer_image,EGL_KHR_gl_texture_2D_image,EGL_KHR_gl_texture_3D_image,EGL_KHR_gl_texture_cubemap_image,EGL_KHR_image,EGL_KHR_image_base,EGL_KHR_image_pixmap,EGL_KHR_lock_surface,EGL_KHR_lock_surface2,EGL_KHR_lock_surface3,EGL_KHR_mutable_render_buffer,EGL_KHR_no_config_context,EGL_KHR_partial_update,EGL_KHR_platform_android,EGL_KHR_platform_gbm,EGL_KHR_platform_wayland,EGL_KHR_platform_x11,EGL_KHR_reusable_sync,EGL_KHR_stream,EGL_KHR_stream_attrib,EGL_KHR_stream_consumer_gltexture,EGL_KHR_stream_cross_process_fd,EGL_KHR_stream_fifo,EGL_KHR_stream_producer_aldatalocator,EGL_KHR_stream_producer_eglsurface,EGL_KHR_surfaceless_context,EGL_KHR_swap_buffers_with_damage,EGL_KHR_vg_parent_image,EGL_KHR_wait_sync,EGL_MESA_drm_image,EGL_MESA_image_dma_buf_export,EGL_MESA_platform_gbm,EGL_MESA_platform_surfaceless,EGL_NOK_swap_region,EGL_NOK_swap_region2,EGL_NOK_texture_from_pixmap,EGL_NV_3dvision_surface,EGL_NV_context_priority_realtime,EGL_NV_coverage_sample,EGL_NV_coverage_sample_resolve,EGL_NV_cuda_event,EGL_NV_depth_nonlinear,EGL_NV_device_cuda,EGL_NV_native_query,EGL_NV_post_convert_rounding,EGL_NV_post_sub_buffer,EGL_NV_robustness_video_memory_purge,EGL_NV_stream_consumer_gltexture_yuv,EGL_NV_stream_cross_display,EGL_NV_stream_cross_object,EGL_NV_stream_cross_partition,EGL_NV_stream_cross_process,EGL_NV_stream_cross_system,EGL_NV_stream_fifo_next,EGL_NV_stream_fifo_synchronous,EGL_NV_stream_flush,EGL_NV_stream_frame_limits,EGL_NV_stream_metadata,EGL_NV_stream_remote,EGL_NV_stream_reset,EGL_NV_stream_socket,EGL_NV_stream_socket_inet,EGL_NV_stream_socket_unix,EGL_NV_stream_sync,EGL_NV_sync,EGL_NV_system_time,EGL_TIZEN_image_native_buffer,EGL_TIZEN_image_native_surface" Online: Too many extensions */ #ifndef __glad_egl_h_ #ifdef __egl_h_ #error EGL header already included, remove this include, glad already provides it #endif #define __glad_egl_h_ #define __egl_h_ #if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 #endif #ifndef NOMINMAX #define NOMINMAX 1 #endif #include #endif #ifndef APIENTRY #define APIENTRY #endif #ifndef APIENTRYP #define APIENTRYP APIENTRY * #endif #ifndef GLAPI #define GLAPI extern #endif #ifdef __cplusplus extern "C" { #endif typedef void* (* GLADloadproc)(const char *name); GLAPI int gladLoadEGL(void); GLAPI int gladLoadEGLLoader(GLADloadproc); #include #include struct AHardwareBuffer; typedef unsigned int EGLBoolean; typedef unsigned int EGLenum; typedef intptr_t EGLAttribKHR; typedef intptr_t EGLAttrib; typedef void *EGLClientBuffer; typedef void *EGLConfig; typedef void *EGLContext; typedef void *EGLDeviceEXT; typedef void *EGLDisplay; typedef void *EGLImage; typedef void *EGLImageKHR; typedef void *EGLLabelKHR; typedef void *EGLObjectKHR; typedef void *EGLOutputLayerEXT; typedef void *EGLOutputPortEXT; typedef void *EGLStreamKHR; typedef void *EGLSurface; typedef void *EGLSync; typedef void *EGLSyncKHR; typedef void *EGLSyncNV; typedef void (*__eglMustCastToProperFunctionPointerType)(void); typedef khronos_utime_nanoseconds_t EGLTimeKHR; typedef khronos_utime_nanoseconds_t EGLTime; typedef khronos_utime_nanoseconds_t EGLTimeNV; typedef khronos_utime_nanoseconds_t EGLuint64NV; typedef khronos_uint64_t EGLuint64KHR; typedef khronos_stime_nanoseconds_t EGLnsecsANDROID; typedef int EGLNativeFileDescriptorKHR; typedef khronos_ssize_t EGLsizeiANDROID; typedef void (*EGLSetBlobFuncANDROID) (const void *key, EGLsizeiANDROID keySize, const void *value, EGLsizeiANDROID valueSize); typedef EGLsizeiANDROID (*EGLGetBlobFuncANDROID) (const void *key, EGLsizeiANDROID keySize, void *value, EGLsizeiANDROID valueSize); struct EGLClientPixmapHI { void *pData; EGLint iWidth; EGLint iHeight; EGLint iStride; }; typedef void (APIENTRY *EGLDEBUGPROCKHR)(EGLenum error,const char *command,EGLint messageType,EGLLabelKHR threadLabel,EGLLabelKHR objectLabel,const char* message); #define EGL_ALPHA_SIZE 0x3021 #define EGL_BAD_ACCESS 0x3002 #define EGL_BAD_ALLOC 0x3003 #define EGL_BAD_ATTRIBUTE 0x3004 #define EGL_BAD_CONFIG 0x3005 #define EGL_BAD_CONTEXT 0x3006 #define EGL_BAD_CURRENT_SURFACE 0x3007 #define EGL_BAD_DISPLAY 0x3008 #define EGL_BAD_MATCH 0x3009 #define EGL_BAD_NATIVE_PIXMAP 0x300A #define EGL_BAD_NATIVE_WINDOW 0x300B #define EGL_BAD_PARAMETER 0x300C #define EGL_BAD_SURFACE 0x300D #define EGL_BLUE_SIZE 0x3022 #define EGL_BUFFER_SIZE 0x3020 #define EGL_CONFIG_CAVEAT 0x3027 #define EGL_CONFIG_ID 0x3028 #define EGL_CORE_NATIVE_ENGINE 0x305B #define EGL_DEPTH_SIZE 0x3025 #define EGL_DONT_CARE EGL_CAST(EGLint,-1) #define EGL_DRAW 0x3059 #define EGL_EXTENSIONS 0x3055 #define EGL_FALSE 0 #define EGL_GREEN_SIZE 0x3023 #define EGL_HEIGHT 0x3056 #define EGL_LARGEST_PBUFFER 0x3058 #define EGL_LEVEL 0x3029 #define EGL_MAX_PBUFFER_HEIGHT 0x302A #define EGL_MAX_PBUFFER_PIXELS 0x302B #define EGL_MAX_PBUFFER_WIDTH 0x302C #define EGL_NATIVE_RENDERABLE 0x302D #define EGL_NATIVE_VISUAL_ID 0x302E #define EGL_NATIVE_VISUAL_TYPE 0x302F #define EGL_NONE 0x3038 #define EGL_NON_CONFORMANT_CONFIG 0x3051 #define EGL_NOT_INITIALIZED 0x3001 #define EGL_NO_CONTEXT EGL_CAST(EGLContext,0) #define EGL_NO_DISPLAY EGL_CAST(EGLDisplay,0) #define EGL_NO_SURFACE EGL_CAST(EGLSurface,0) #define EGL_PBUFFER_BIT 0x0001 #define EGL_PIXMAP_BIT 0x0002 #define EGL_READ 0x305A #define EGL_RED_SIZE 0x3024 #define EGL_SAMPLES 0x3031 #define EGL_SAMPLE_BUFFERS 0x3032 #define EGL_SLOW_CONFIG 0x3050 #define EGL_STENCIL_SIZE 0x3026 #define EGL_SUCCESS 0x3000 #define EGL_SURFACE_TYPE 0x3033 #define EGL_TRANSPARENT_BLUE_VALUE 0x3035 #define EGL_TRANSPARENT_GREEN_VALUE 0x3036 #define EGL_TRANSPARENT_RED_VALUE 0x3037 #define EGL_TRANSPARENT_RGB 0x3052 #define EGL_TRANSPARENT_TYPE 0x3034 #define EGL_TRUE 1 #define EGL_VENDOR 0x3053 #define EGL_VERSION 0x3054 #define EGL_WIDTH 0x3057 #define EGL_WINDOW_BIT 0x0004 #define EGL_BACK_BUFFER 0x3084 #define EGL_BIND_TO_TEXTURE_RGB 0x3039 #define EGL_BIND_TO_TEXTURE_RGBA 0x303A #define EGL_CONTEXT_LOST 0x300E #define EGL_MIN_SWAP_INTERVAL 0x303B #define EGL_MAX_SWAP_INTERVAL 0x303C #define EGL_MIPMAP_TEXTURE 0x3082 #define EGL_MIPMAP_LEVEL 0x3083 #define EGL_NO_TEXTURE 0x305C #define EGL_TEXTURE_2D 0x305F #define EGL_TEXTURE_FORMAT 0x3080 #define EGL_TEXTURE_RGB 0x305D #define EGL_TEXTURE_RGBA 0x305E #define EGL_TEXTURE_TARGET 0x3081 #define EGL_ALPHA_FORMAT 0x3088 #define EGL_ALPHA_FORMAT_NONPRE 0x308B #define EGL_ALPHA_FORMAT_PRE 0x308C #define EGL_ALPHA_MASK_SIZE 0x303E #define EGL_BUFFER_PRESERVED 0x3094 #define EGL_BUFFER_DESTROYED 0x3095 #define EGL_CLIENT_APIS 0x308D #define EGL_COLORSPACE 0x3087 #define EGL_COLORSPACE_sRGB 0x3089 #define EGL_COLORSPACE_LINEAR 0x308A #define EGL_COLOR_BUFFER_TYPE 0x303F #define EGL_CONTEXT_CLIENT_TYPE 0x3097 #define EGL_DISPLAY_SCALING 10000 #define EGL_HORIZONTAL_RESOLUTION 0x3090 #define EGL_LUMINANCE_BUFFER 0x308F #define EGL_LUMINANCE_SIZE 0x303D #define EGL_OPENGL_ES_BIT 0x0001 #define EGL_OPENVG_BIT 0x0002 #define EGL_OPENGL_ES_API 0x30A0 #define EGL_OPENVG_API 0x30A1 #define EGL_OPENVG_IMAGE 0x3096 #define EGL_PIXEL_ASPECT_RATIO 0x3092 #define EGL_RENDERABLE_TYPE 0x3040 #define EGL_RENDER_BUFFER 0x3086 #define EGL_RGB_BUFFER 0x308E #define EGL_SINGLE_BUFFER 0x3085 #define EGL_SWAP_BEHAVIOR 0x3093 #define EGL_UNKNOWN EGL_CAST(EGLint,-1) #define EGL_VERTICAL_RESOLUTION 0x3091 #define EGL_CONFORMANT 0x3042 #define EGL_CONTEXT_CLIENT_VERSION 0x3098 #define EGL_MATCH_NATIVE_PIXMAP 0x3041 #define EGL_OPENGL_ES2_BIT 0x0004 #define EGL_VG_ALPHA_FORMAT 0x3088 #define EGL_VG_ALPHA_FORMAT_NONPRE 0x308B #define EGL_VG_ALPHA_FORMAT_PRE 0x308C #define EGL_VG_ALPHA_FORMAT_PRE_BIT 0x0040 #define EGL_VG_COLORSPACE 0x3087 #define EGL_VG_COLORSPACE_sRGB 0x3089 #define EGL_VG_COLORSPACE_LINEAR 0x308A #define EGL_VG_COLORSPACE_LINEAR_BIT 0x0020 #define EGL_DEFAULT_DISPLAY EGL_CAST(EGLNativeDisplayType,0) #define EGL_MULTISAMPLE_RESOLVE_BOX_BIT 0x0200 #define EGL_MULTISAMPLE_RESOLVE 0x3099 #define EGL_MULTISAMPLE_RESOLVE_DEFAULT 0x309A #define EGL_MULTISAMPLE_RESOLVE_BOX 0x309B #define EGL_OPENGL_API 0x30A2 #define EGL_OPENGL_BIT 0x0008 #define EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400 #define EGL_CONTEXT_MAJOR_VERSION 0x3098 #define EGL_CONTEXT_MINOR_VERSION 0x30FB #define EGL_CONTEXT_OPENGL_PROFILE_MASK 0x30FD #define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY 0x31BD #define EGL_NO_RESET_NOTIFICATION 0x31BE #define EGL_LOSE_CONTEXT_ON_RESET 0x31BF #define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001 #define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT 0x00000002 #define EGL_CONTEXT_OPENGL_DEBUG 0x31B0 #define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE 0x31B1 #define EGL_CONTEXT_OPENGL_ROBUST_ACCESS 0x31B2 #define EGL_OPENGL_ES3_BIT 0x00000040 #define EGL_CL_EVENT_HANDLE 0x309C #define EGL_SYNC_CL_EVENT 0x30FE #define EGL_SYNC_CL_EVENT_COMPLETE 0x30FF #define EGL_SYNC_PRIOR_COMMANDS_COMPLETE 0x30F0 #define EGL_SYNC_TYPE 0x30F7 #define EGL_SYNC_STATUS 0x30F1 #define EGL_SYNC_CONDITION 0x30F8 #define EGL_SIGNALED 0x30F2 #define EGL_UNSIGNALED 0x30F3 #define EGL_SYNC_FLUSH_COMMANDS_BIT 0x0001 #define EGL_FOREVER 0xFFFFFFFFFFFFFFFF #define EGL_TIMEOUT_EXPIRED 0x30F5 #define EGL_CONDITION_SATISFIED 0x30F6 #define EGL_NO_SYNC EGL_CAST(EGLSync,0) #define EGL_SYNC_FENCE 0x30F9 #define EGL_GL_COLORSPACE 0x309D #define EGL_GL_COLORSPACE_SRGB 0x3089 #define EGL_GL_COLORSPACE_LINEAR 0x308A #define EGL_GL_RENDERBUFFER 0x30B9 #define EGL_GL_TEXTURE_2D 0x30B1 #define EGL_GL_TEXTURE_LEVEL 0x30BC #define EGL_GL_TEXTURE_3D 0x30B2 #define EGL_GL_TEXTURE_ZOFFSET 0x30BD #define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x30B3 #define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x30B4 #define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x30B5 #define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x30B6 #define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x30B7 #define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8 #define EGL_IMAGE_PRESERVED 0x30D2 #define EGL_NO_IMAGE EGL_CAST(EGLImage,0) EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config); EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target); EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list); EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list); EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list); EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list); EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx); EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface); EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value); EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config); EGLDisplay eglGetCurrentDisplay(void); EGLSurface eglGetCurrentSurface(EGLint readdraw); EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id); EGLint eglGetError(void); __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname); EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor); EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value); const char *eglQueryString(EGLDisplay dpy, EGLint name); EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value); EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface); EGLBoolean eglTerminate(EGLDisplay dpy); EGLBoolean eglWaitGL(void); EGLBoolean eglWaitNative(EGLint engine); EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer); EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer); EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value); EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval); EGLBoolean eglBindAPI(EGLenum api); EGLenum eglQueryAPI(void); EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list); EGLBoolean eglReleaseThread(void); EGLBoolean eglWaitClient(void); EGLContext eglGetCurrentContext(void); EGLSync eglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list); EGLBoolean eglDestroySync(EGLDisplay dpy, EGLSync sync); EGLint eglClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout); EGLBoolean eglGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value); EGLImage eglCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list); EGLBoolean eglDestroyImage(EGLDisplay dpy, EGLImage image); EGLDisplay eglGetPlatformDisplay(EGLenum platform, void *native_display, const EGLAttrib *attrib_list); EGLSurface eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list); EGLSurface eglCreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list); EGLBoolean eglWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags); #define EGL_NATIVE_BUFFER_USAGE_ANDROID 0x3143 #define EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID 0x00000001 #define EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID 0x00000002 #define EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID 0x00000004 #define EGL_FRAMEBUFFER_TARGET_ANDROID 0x3147 #define EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID 0x314C #define EGL_TIMESTAMP_PENDING_ANDROID EGL_CAST(EGLnsecsANDROID,-2) #define EGL_TIMESTAMP_INVALID_ANDROID EGL_CAST(EGLnsecsANDROID,-1) #define EGL_TIMESTAMPS_ANDROID 0x3430 #define EGL_COMPOSITE_DEADLINE_ANDROID 0x3431 #define EGL_COMPOSITE_INTERVAL_ANDROID 0x3432 #define EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID 0x3433 #define EGL_REQUESTED_PRESENT_TIME_ANDROID 0x3434 #define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x3435 #define EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3436 #define EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3437 #define EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3438 #define EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID 0x3439 #define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x343A #define EGL_DEQUEUE_READY_TIME_ANDROID 0x343B #define EGL_READS_DONE_TIME_ANDROID 0x343C #define EGL_NATIVE_BUFFER_ANDROID 0x3140 #define EGL_SYNC_NATIVE_FENCE_ANDROID 0x3144 #define EGL_SYNC_NATIVE_FENCE_FD_ANDROID 0x3145 #define EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID 0x3146 #define EGL_NO_NATIVE_FENCE_FD_ANDROID -1 #define EGL_RECORDABLE_ANDROID 0x3142 #define EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE 0x3200 #define EGL_D3D9_DEVICE_ANGLE 0x33A0 #define EGL_D3D11_DEVICE_ANGLE 0x33A1 #define EGL_FIXED_SIZE_ANGLE 0x3201 #define EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM 0x328A #define EGL_DISCARD_SAMPLES_ARM 0x3286 #define EGL_FRONT_BUFFER_EXT 0x3464 #define EGL_BUFFER_AGE_EXT 0x313D #define EGL_SYNC_CLIENT_EXT 0x3364 #define EGL_SYNC_CLIENT_SIGNAL_EXT 0x3365 #define EGL_PRIMARY_COMPOSITOR_CONTEXT_EXT 0x3460 #define EGL_EXTERNAL_REF_ID_EXT 0x3461 #define EGL_COMPOSITOR_DROP_NEWEST_FRAME_EXT 0x3462 #define EGL_COMPOSITOR_KEEP_NEWEST_FRAME_EXT 0x3463 #define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT 0x30BF #define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT 0x3138 #define EGL_NO_RESET_NOTIFICATION_EXT 0x31BE #define EGL_LOSE_CONTEXT_ON_RESET_EXT 0x31BF #define EGL_NO_DEVICE_EXT EGL_CAST(EGLDeviceEXT,0) #define EGL_BAD_DEVICE_EXT 0x322B #define EGL_DEVICE_EXT 0x322C #define EGL_DRM_DEVICE_FILE_EXT 0x3233 #define EGL_DRM_MASTER_FD_EXT 0x333C #define EGL_OPENWF_DEVICE_ID_EXT 0x3237 #define EGL_GL_COLORSPACE_BT2020_LINEAR_EXT 0x333F #define EGL_GL_COLORSPACE_BT2020_PQ_EXT 0x3340 #define EGL_GL_COLORSPACE_DISPLAY_P3_EXT 0x3363 #define EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT 0x3362 #define EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT 0x3490 #define EGL_GL_COLORSPACE_SCRGB_EXT 0x3351 #define EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT 0x3350 #define EGL_LINUX_DMA_BUF_EXT 0x3270 #define EGL_LINUX_DRM_FOURCC_EXT 0x3271 #define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272 #define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273 #define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274 #define EGL_DMA_BUF_PLANE1_FD_EXT 0x3275 #define EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276 #define EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277 #define EGL_DMA_BUF_PLANE2_FD_EXT 0x3278 #define EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279 #define EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A #define EGL_YUV_COLOR_SPACE_HINT_EXT 0x327B #define EGL_SAMPLE_RANGE_HINT_EXT 0x327C #define EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT 0x327D #define EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT 0x327E #define EGL_ITU_REC601_EXT 0x327F #define EGL_ITU_REC709_EXT 0x3280 #define EGL_ITU_REC2020_EXT 0x3281 #define EGL_YUV_FULL_RANGE_EXT 0x3282 #define EGL_YUV_NARROW_RANGE_EXT 0x3283 #define EGL_YUV_CHROMA_SITING_0_EXT 0x3284 #define EGL_YUV_CHROMA_SITING_0_5_EXT 0x3285 #define EGL_DMA_BUF_PLANE3_FD_EXT 0x3440 #define EGL_DMA_BUF_PLANE3_OFFSET_EXT 0x3441 #define EGL_DMA_BUF_PLANE3_PITCH_EXT 0x3442 #define EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT 0x3443 #define EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT 0x3444 #define EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT 0x3445 #define EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT 0x3446 #define EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT 0x3447 #define EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT 0x3448 #define EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT 0x3449 #define EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT 0x344A #define EGL_GL_COLORSPACE_DEFAULT_EXT 0x314D #define EGL_IMPORT_SYNC_TYPE_EXT 0x3470 #define EGL_IMPORT_IMPLICIT_SYNC_EXT 0x3471 #define EGL_IMPORT_EXPLICIT_SYNC_EXT 0x3472 #define EGL_MULTIVIEW_VIEW_COUNT_EXT 0x3134 #define EGL_NO_OUTPUT_LAYER_EXT EGL_CAST(EGLOutputLayerEXT,0) #define EGL_NO_OUTPUT_PORT_EXT EGL_CAST(EGLOutputPortEXT,0) #define EGL_BAD_OUTPUT_LAYER_EXT 0x322D #define EGL_BAD_OUTPUT_PORT_EXT 0x322E #define EGL_SWAP_INTERVAL_EXT 0x322F #define EGL_DRM_CRTC_EXT 0x3234 #define EGL_DRM_PLANE_EXT 0x3235 #define EGL_DRM_CONNECTOR_EXT 0x3236 #define EGL_OPENWF_PIPELINE_ID_EXT 0x3238 #define EGL_OPENWF_PORT_ID_EXT 0x3239 #define EGL_COLOR_COMPONENT_TYPE_EXT 0x3339 #define EGL_COLOR_COMPONENT_TYPE_FIXED_EXT 0x333A #define EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT 0x333B #define EGL_PLATFORM_DEVICE_EXT 0x313F #define EGL_PLATFORM_WAYLAND_EXT 0x31D8 #define EGL_PLATFORM_X11_EXT 0x31D5 #define EGL_PLATFORM_X11_SCREEN_EXT 0x31D6 #define EGL_PROTECTED_CONTENT_EXT 0x32C0 #define EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT 0x3360 #define EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT 0x3361 #define EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT 0x3341 #define EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT 0x3342 #define EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT 0x3343 #define EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT 0x3344 #define EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT 0x3345 #define EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT 0x3346 #define EGL_SMPTE2086_WHITE_POINT_X_EXT 0x3347 #define EGL_SMPTE2086_WHITE_POINT_Y_EXT 0x3348 #define EGL_SMPTE2086_MAX_LUMINANCE_EXT 0x3349 #define EGL_SMPTE2086_MIN_LUMINANCE_EXT 0x334A #define EGL_METADATA_SCALING_EXT 50000 #define EGL_YUV_ORDER_EXT 0x3301 #define EGL_YUV_NUMBER_OF_PLANES_EXT 0x3311 #define EGL_YUV_SUBSAMPLE_EXT 0x3312 #define EGL_YUV_DEPTH_RANGE_EXT 0x3317 #define EGL_YUV_CSC_STANDARD_EXT 0x330A #define EGL_YUV_PLANE_BPP_EXT 0x331A #define EGL_YUV_BUFFER_EXT 0x3300 #define EGL_YUV_ORDER_YUV_EXT 0x3302 #define EGL_YUV_ORDER_YVU_EXT 0x3303 #define EGL_YUV_ORDER_YUYV_EXT 0x3304 #define EGL_YUV_ORDER_UYVY_EXT 0x3305 #define EGL_YUV_ORDER_YVYU_EXT 0x3306 #define EGL_YUV_ORDER_VYUY_EXT 0x3307 #define EGL_YUV_ORDER_AYUV_EXT 0x3308 #define EGL_YUV_SUBSAMPLE_4_2_0_EXT 0x3313 #define EGL_YUV_SUBSAMPLE_4_2_2_EXT 0x3314 #define EGL_YUV_SUBSAMPLE_4_4_4_EXT 0x3315 #define EGL_YUV_DEPTH_RANGE_LIMITED_EXT 0x3318 #define EGL_YUV_DEPTH_RANGE_FULL_EXT 0x3319 #define EGL_YUV_CSC_STANDARD_601_EXT 0x330B #define EGL_YUV_CSC_STANDARD_709_EXT 0x330C #define EGL_YUV_CSC_STANDARD_2020_EXT 0x330D #define EGL_YUV_PLANE_BPP_0_EXT 0x331B #define EGL_YUV_PLANE_BPP_8_EXT 0x331C #define EGL_YUV_PLANE_BPP_10_EXT 0x331D #define EGL_CLIENT_PIXMAP_POINTER_HI 0x8F74 #define EGL_COLOR_FORMAT_HI 0x8F70 #define EGL_COLOR_RGB_HI 0x8F71 #define EGL_COLOR_RGBA_HI 0x8F72 #define EGL_COLOR_ARGB_HI 0x8F73 #define EGL_CONTEXT_PRIORITY_LEVEL_IMG 0x3100 #define EGL_CONTEXT_PRIORITY_HIGH_IMG 0x3101 #define EGL_CONTEXT_PRIORITY_MEDIUM_IMG 0x3102 #define EGL_CONTEXT_PRIORITY_LOW_IMG 0x3103 #define EGL_NATIVE_BUFFER_MULTIPLANE_SEPARATE_IMG 0x3105 #define EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG 0x3106 #define EGL_CL_EVENT_HANDLE_KHR 0x309C #define EGL_SYNC_CL_EVENT_KHR 0x30FE #define EGL_SYNC_CL_EVENT_COMPLETE_KHR 0x30FF #define EGL_CONFORMANT_KHR 0x3042 #define EGL_VG_COLORSPACE_LINEAR_BIT_KHR 0x0020 #define EGL_VG_ALPHA_FORMAT_PRE_BIT_KHR 0x0040 #define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0 #define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 #define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 #define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 #define EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB #define EGL_CONTEXT_FLAGS_KHR 0x30FC #define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD #define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31BD #define EGL_NO_RESET_NOTIFICATION_KHR 0x31BE #define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31BF #define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 #define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 #define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 #define EGL_OPENGL_ES3_BIT_KHR 0x00000040 #define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31B3 #define EGL_OBJECT_THREAD_KHR 0x33B0 #define EGL_OBJECT_DISPLAY_KHR 0x33B1 #define EGL_OBJECT_CONTEXT_KHR 0x33B2 #define EGL_OBJECT_SURFACE_KHR 0x33B3 #define EGL_OBJECT_IMAGE_KHR 0x33B4 #define EGL_OBJECT_SYNC_KHR 0x33B5 #define EGL_OBJECT_STREAM_KHR 0x33B6 #define EGL_DEBUG_MSG_CRITICAL_KHR 0x33B9 #define EGL_DEBUG_MSG_ERROR_KHR 0x33BA #define EGL_DEBUG_MSG_WARN_KHR 0x33BB #define EGL_DEBUG_MSG_INFO_KHR 0x33BC #define EGL_DEBUG_CALLBACK_KHR 0x33B8 #define EGL_TRACK_REFERENCES_KHR 0x3352 #define EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR 0x30F0 #define EGL_SYNC_CONDITION_KHR 0x30F8 #define EGL_SYNC_FENCE_KHR 0x30F9 #define EGL_GL_COLORSPACE_KHR 0x309D #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 #define EGL_GL_COLORSPACE_LINEAR_KHR 0x308A #define EGL_GL_RENDERBUFFER_KHR 0x30B9 #define EGL_GL_TEXTURE_2D_KHR 0x30B1 #define EGL_GL_TEXTURE_LEVEL_KHR 0x30BC #define EGL_GL_TEXTURE_3D_KHR 0x30B2 #define EGL_GL_TEXTURE_ZOFFSET_KHR 0x30BD #define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR 0x30B3 #define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR 0x30B4 #define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR 0x30B5 #define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR 0x30B6 #define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR 0x30B7 #define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR 0x30B8 #define EGL_NATIVE_PIXMAP_KHR 0x30B0 #define EGL_NO_IMAGE_KHR EGL_CAST(EGLImageKHR,0) #define EGL_IMAGE_PRESERVED_KHR 0x30D2 #define EGL_READ_SURFACE_BIT_KHR 0x0001 #define EGL_WRITE_SURFACE_BIT_KHR 0x0002 #define EGL_LOCK_SURFACE_BIT_KHR 0x0080 #define EGL_OPTIMAL_FORMAT_BIT_KHR 0x0100 #define EGL_MATCH_FORMAT_KHR 0x3043 #define EGL_FORMAT_RGB_565_EXACT_KHR 0x30C0 #define EGL_FORMAT_RGB_565_KHR 0x30C1 #define EGL_FORMAT_RGBA_8888_EXACT_KHR 0x30C2 #define EGL_FORMAT_RGBA_8888_KHR 0x30C3 #define EGL_MAP_PRESERVE_PIXELS_KHR 0x30C4 #define EGL_LOCK_USAGE_HINT_KHR 0x30C5 #define EGL_BITMAP_POINTER_KHR 0x30C6 #define EGL_BITMAP_PITCH_KHR 0x30C7 #define EGL_BITMAP_ORIGIN_KHR 0x30C8 #define EGL_BITMAP_PIXEL_RED_OFFSET_KHR 0x30C9 #define EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR 0x30CA #define EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR 0x30CB #define EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR 0x30CC #define EGL_BITMAP_PIXEL_LUMINANCE_OFFSET_KHR 0x30CD #define EGL_LOWER_LEFT_KHR 0x30CE #define EGL_UPPER_LEFT_KHR 0x30CF #define EGL_BITMAP_PIXEL_SIZE_KHR 0x3110 #define EGL_MUTABLE_RENDER_BUFFER_BIT_KHR 0x1000 #define EGL_NO_CONFIG_KHR EGL_CAST(EGLConfig,0) #define EGL_BUFFER_AGE_KHR 0x313D #define EGL_PLATFORM_ANDROID_KHR 0x3141 #define EGL_PLATFORM_GBM_KHR 0x31D7 #define EGL_PLATFORM_WAYLAND_KHR 0x31D8 #define EGL_PLATFORM_X11_KHR 0x31D5 #define EGL_PLATFORM_X11_SCREEN_KHR 0x31D6 #define EGL_SYNC_STATUS_KHR 0x30F1 #define EGL_SIGNALED_KHR 0x30F2 #define EGL_UNSIGNALED_KHR 0x30F3 #define EGL_TIMEOUT_EXPIRED_KHR 0x30F5 #define EGL_CONDITION_SATISFIED_KHR 0x30F6 #define EGL_SYNC_TYPE_KHR 0x30F7 #define EGL_SYNC_REUSABLE_KHR 0x30FA #define EGL_SYNC_FLUSH_COMMANDS_BIT_KHR 0x0001 #define EGL_FOREVER_KHR 0xFFFFFFFFFFFFFFFF #define EGL_NO_SYNC_KHR EGL_CAST(EGLSyncKHR,0) #define EGL_NO_STREAM_KHR EGL_CAST(EGLStreamKHR,0) #define EGL_CONSUMER_LATENCY_USEC_KHR 0x3210 #define EGL_PRODUCER_FRAME_KHR 0x3212 #define EGL_CONSUMER_FRAME_KHR 0x3213 #define EGL_STREAM_STATE_KHR 0x3214 #define EGL_STREAM_STATE_CREATED_KHR 0x3215 #define EGL_STREAM_STATE_CONNECTING_KHR 0x3216 #define EGL_STREAM_STATE_EMPTY_KHR 0x3217 #define EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR 0x3218 #define EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR 0x3219 #define EGL_STREAM_STATE_DISCONNECTED_KHR 0x321A #define EGL_BAD_STREAM_KHR 0x321B #define EGL_BAD_STATE_KHR 0x321C #define EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR 0x321E #define EGL_NO_FILE_DESCRIPTOR_KHR EGL_CAST(EGLNativeFileDescriptorKHR,-1) #define EGL_STREAM_FIFO_LENGTH_KHR 0x31FC #define EGL_STREAM_TIME_NOW_KHR 0x31FD #define EGL_STREAM_TIME_CONSUMER_KHR 0x31FE #define EGL_STREAM_TIME_PRODUCER_KHR 0x31FF #define EGL_STREAM_BIT_KHR 0x0800 #define EGL_VG_PARENT_IMAGE_KHR 0x30BA #define EGL_DRM_BUFFER_FORMAT_MESA 0x31D0 #define EGL_DRM_BUFFER_USE_MESA 0x31D1 #define EGL_DRM_BUFFER_FORMAT_ARGB32_MESA 0x31D2 #define EGL_DRM_BUFFER_MESA 0x31D3 #define EGL_DRM_BUFFER_STRIDE_MESA 0x31D4 #define EGL_DRM_BUFFER_USE_SCANOUT_MESA 0x00000001 #define EGL_DRM_BUFFER_USE_SHARE_MESA 0x00000002 #define EGL_DRM_BUFFER_USE_CURSOR_MESA 0x00000004 #define EGL_PLATFORM_GBM_MESA 0x31D7 #define EGL_PLATFORM_SURFACELESS_MESA 0x31DD #define EGL_Y_INVERTED_NOK 0x307F #define EGL_AUTO_STEREO_NV 0x3136 #define EGL_CONTEXT_PRIORITY_REALTIME_NV 0x3357 #define EGL_COVERAGE_BUFFERS_NV 0x30E0 #define EGL_COVERAGE_SAMPLES_NV 0x30E1 #define EGL_COVERAGE_SAMPLE_RESOLVE_NV 0x3131 #define EGL_COVERAGE_SAMPLE_RESOLVE_DEFAULT_NV 0x3132 #define EGL_COVERAGE_SAMPLE_RESOLVE_NONE_NV 0x3133 #define EGL_CUDA_EVENT_HANDLE_NV 0x323B #define EGL_SYNC_CUDA_EVENT_NV 0x323C #define EGL_SYNC_CUDA_EVENT_COMPLETE_NV 0x323D #define EGL_DEPTH_ENCODING_NV 0x30E2 #define EGL_DEPTH_ENCODING_NONE_NV 0 #define EGL_DEPTH_ENCODING_NONLINEAR_NV 0x30E3 #define EGL_CUDA_DEVICE_NV 0x323A #define EGL_POST_SUB_BUFFER_SUPPORTED_NV 0x30BE #define EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x334C #define EGL_YUV_PLANE0_TEXTURE_UNIT_NV 0x332C #define EGL_YUV_PLANE1_TEXTURE_UNIT_NV 0x332D #define EGL_YUV_PLANE2_TEXTURE_UNIT_NV 0x332E #define EGL_STREAM_CROSS_DISPLAY_NV 0x334E #define EGL_STREAM_CROSS_OBJECT_NV 0x334D #define EGL_STREAM_CROSS_PARTITION_NV 0x323F #define EGL_STREAM_CROSS_PROCESS_NV 0x3245 #define EGL_STREAM_CROSS_SYSTEM_NV 0x334F #define EGL_PENDING_FRAME_NV 0x3329 #define EGL_STREAM_TIME_PENDING_NV 0x332A #define EGL_STREAM_FIFO_SYNCHRONOUS_NV 0x3336 #define EGL_PRODUCER_MAX_FRAME_HINT_NV 0x3337 #define EGL_CONSUMER_MAX_FRAME_HINT_NV 0x3338 #define EGL_MAX_STREAM_METADATA_BLOCKS_NV 0x3250 #define EGL_MAX_STREAM_METADATA_BLOCK_SIZE_NV 0x3251 #define EGL_MAX_STREAM_METADATA_TOTAL_SIZE_NV 0x3252 #define EGL_PRODUCER_METADATA_NV 0x3253 #define EGL_CONSUMER_METADATA_NV 0x3254 #define EGL_PENDING_METADATA_NV 0x3328 #define EGL_METADATA0_SIZE_NV 0x3255 #define EGL_METADATA1_SIZE_NV 0x3256 #define EGL_METADATA2_SIZE_NV 0x3257 #define EGL_METADATA3_SIZE_NV 0x3258 #define EGL_METADATA0_TYPE_NV 0x3259 #define EGL_METADATA1_TYPE_NV 0x325A #define EGL_METADATA2_TYPE_NV 0x325B #define EGL_METADATA3_TYPE_NV 0x325C #define EGL_STREAM_STATE_INITIALIZING_NV 0x3240 #define EGL_STREAM_TYPE_NV 0x3241 #define EGL_STREAM_PROTOCOL_NV 0x3242 #define EGL_STREAM_ENDPOINT_NV 0x3243 #define EGL_STREAM_LOCAL_NV 0x3244 #define EGL_STREAM_PRODUCER_NV 0x3247 #define EGL_STREAM_CONSUMER_NV 0x3248 #define EGL_STREAM_PROTOCOL_FD_NV 0x3246 #define EGL_SUPPORT_RESET_NV 0x3334 #define EGL_SUPPORT_REUSE_NV 0x3335 #define EGL_STREAM_PROTOCOL_SOCKET_NV 0x324B #define EGL_SOCKET_HANDLE_NV 0x324C #define EGL_SOCKET_TYPE_NV 0x324D #define EGL_SOCKET_TYPE_INET_NV 0x324F #define EGL_SOCKET_TYPE_UNIX_NV 0x324E #define EGL_SYNC_NEW_FRAME_NV 0x321F #define EGL_SYNC_PRIOR_COMMANDS_COMPLETE_NV 0x30E6 #define EGL_SYNC_STATUS_NV 0x30E7 #define EGL_SIGNALED_NV 0x30E8 #define EGL_UNSIGNALED_NV 0x30E9 #define EGL_SYNC_FLUSH_COMMANDS_BIT_NV 0x0001 #define EGL_FOREVER_NV 0xFFFFFFFFFFFFFFFF #define EGL_ALREADY_SIGNALED_NV 0x30EA #define EGL_TIMEOUT_EXPIRED_NV 0x30EB #define EGL_CONDITION_SATISFIED_NV 0x30EC #define EGL_SYNC_TYPE_NV 0x30ED #define EGL_SYNC_CONDITION_NV 0x30EE #define EGL_SYNC_FENCE_NV 0x30EF #define EGL_NO_SYNC_NV EGL_CAST(EGLSyncNV,0) #define EGL_NATIVE_BUFFER_TIZEN 0x32A0 #define EGL_NATIVE_SURFACE_TIZEN 0x32A1 #ifndef EGL_ANDROID_blob_cache #define EGL_ANDROID_blob_cache 1 typedef void (APIENTRYP PFNEGLSETBLOBCACHEFUNCSANDROIDPROC)(EGLDisplay dpy, EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get); GLAPI PFNEGLSETBLOBCACHEFUNCSANDROIDPROC glad_eglSetBlobCacheFuncsANDROID; #define eglSetBlobCacheFuncsANDROID glad_eglSetBlobCacheFuncsANDROID #endif #ifndef EGL_ANDROID_create_native_client_buffer #define EGL_ANDROID_create_native_client_buffer 1 typedef EGLClientBuffer (APIENTRYP PFNEGLCREATENATIVECLIENTBUFFERANDROIDPROC)(const EGLint *attrib_list); GLAPI PFNEGLCREATENATIVECLIENTBUFFERANDROIDPROC glad_eglCreateNativeClientBufferANDROID; #define eglCreateNativeClientBufferANDROID glad_eglCreateNativeClientBufferANDROID #endif #ifndef EGL_ANDROID_framebuffer_target #define EGL_ANDROID_framebuffer_target 1 #endif #ifndef EGL_ANDROID_front_buffer_auto_refresh #define EGL_ANDROID_front_buffer_auto_refresh 1 #endif #ifndef EGL_ANDROID_get_frame_timestamps #define EGL_ANDROID_get_frame_timestamps 1 typedef EGLBoolean (APIENTRYP PFNEGLGETCOMPOSITORTIMINGSUPPORTEDANDROIDPROC)(EGLDisplay dpy, EGLSurface surface, EGLint name); GLAPI PFNEGLGETCOMPOSITORTIMINGSUPPORTEDANDROIDPROC glad_eglGetCompositorTimingSupportedANDROID; #define eglGetCompositorTimingSupportedANDROID glad_eglGetCompositorTimingSupportedANDROID typedef EGLBoolean (APIENTRYP PFNEGLGETCOMPOSITORTIMINGANDROIDPROC)(EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values); GLAPI PFNEGLGETCOMPOSITORTIMINGANDROIDPROC glad_eglGetCompositorTimingANDROID; #define eglGetCompositorTimingANDROID glad_eglGetCompositorTimingANDROID typedef EGLBoolean (APIENTRYP PFNEGLGETNEXTFRAMEIDANDROIDPROC)(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *frameId); GLAPI PFNEGLGETNEXTFRAMEIDANDROIDPROC glad_eglGetNextFrameIdANDROID; #define eglGetNextFrameIdANDROID glad_eglGetNextFrameIdANDROID typedef EGLBoolean (APIENTRYP PFNEGLGETFRAMETIMESTAMPSUPPORTEDANDROIDPROC)(EGLDisplay dpy, EGLSurface surface, EGLint timestamp); GLAPI PFNEGLGETFRAMETIMESTAMPSUPPORTEDANDROIDPROC glad_eglGetFrameTimestampSupportedANDROID; #define eglGetFrameTimestampSupportedANDROID glad_eglGetFrameTimestampSupportedANDROID typedef EGLBoolean (APIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROIDPROC)(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values); GLAPI PFNEGLGETFRAMETIMESTAMPSANDROIDPROC glad_eglGetFrameTimestampsANDROID; #define eglGetFrameTimestampsANDROID glad_eglGetFrameTimestampsANDROID #endif #ifndef EGL_ANDROID_get_native_client_buffer #define EGL_ANDROID_get_native_client_buffer 1 typedef EGLClientBuffer (APIENTRYP PFNEGLGETNATIVECLIENTBUFFERANDROIDPROC)(const struct AHardwareBuffer *buffer); GLAPI PFNEGLGETNATIVECLIENTBUFFERANDROIDPROC glad_eglGetNativeClientBufferANDROID; #define eglGetNativeClientBufferANDROID glad_eglGetNativeClientBufferANDROID #endif #ifndef EGL_ANDROID_image_native_buffer #define EGL_ANDROID_image_native_buffer 1 #endif #ifndef EGL_ANDROID_native_fence_sync #define EGL_ANDROID_native_fence_sync 1 typedef EGLint (APIENTRYP PFNEGLDUPNATIVEFENCEFDANDROIDPROC)(EGLDisplay dpy, EGLSyncKHR sync); GLAPI PFNEGLDUPNATIVEFENCEFDANDROIDPROC glad_eglDupNativeFenceFDANDROID; #define eglDupNativeFenceFDANDROID glad_eglDupNativeFenceFDANDROID #endif #ifndef EGL_ANDROID_presentation_time #define EGL_ANDROID_presentation_time 1 typedef EGLBoolean (APIENTRYP PFNEGLPRESENTATIONTIMEANDROIDPROC)(EGLDisplay dpy, EGLSurface surface, EGLnsecsANDROID time); GLAPI PFNEGLPRESENTATIONTIMEANDROIDPROC glad_eglPresentationTimeANDROID; #define eglPresentationTimeANDROID glad_eglPresentationTimeANDROID #endif #ifndef EGL_ANDROID_recordable #define EGL_ANDROID_recordable 1 #endif #ifndef EGL_ANGLE_d3d_share_handle_client_buffer #define EGL_ANGLE_d3d_share_handle_client_buffer 1 #endif #ifndef EGL_ANGLE_device_d3d #define EGL_ANGLE_device_d3d 1 #endif #ifndef EGL_ANGLE_query_surface_pointer #define EGL_ANGLE_query_surface_pointer 1 typedef EGLBoolean (APIENTRYP PFNEGLQUERYSURFACEPOINTERANGLEPROC)(EGLDisplay dpy, EGLSurface surface, EGLint attribute, void **value); GLAPI PFNEGLQUERYSURFACEPOINTERANGLEPROC glad_eglQuerySurfacePointerANGLE; #define eglQuerySurfacePointerANGLE glad_eglQuerySurfacePointerANGLE #endif #ifndef EGL_ANGLE_surface_d3d_texture_2d_share_handle #define EGL_ANGLE_surface_d3d_texture_2d_share_handle 1 #endif #ifndef EGL_ANGLE_window_fixed_size #define EGL_ANGLE_window_fixed_size 1 #endif #ifndef EGL_ARM_implicit_external_sync #define EGL_ARM_implicit_external_sync 1 #endif #ifndef EGL_ARM_pixmap_multisample_discard #define EGL_ARM_pixmap_multisample_discard 1 #endif #ifndef EGL_EXT_bind_to_front #define EGL_EXT_bind_to_front 1 #endif #ifndef EGL_EXT_buffer_age #define EGL_EXT_buffer_age 1 #endif #ifndef EGL_EXT_client_extensions #define EGL_EXT_client_extensions 1 #endif #ifndef EGL_EXT_client_sync #define EGL_EXT_client_sync 1 typedef EGLBoolean (APIENTRYP PFNEGLCLIENTSIGNALSYNCEXTPROC)(EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list); GLAPI PFNEGLCLIENTSIGNALSYNCEXTPROC glad_eglClientSignalSyncEXT; #define eglClientSignalSyncEXT glad_eglClientSignalSyncEXT #endif #ifndef EGL_EXT_compositor #define EGL_EXT_compositor 1 typedef EGLBoolean (APIENTRYP PFNEGLCOMPOSITORSETCONTEXTLISTEXTPROC)(const EGLint *external_ref_ids, EGLint num_entries); GLAPI PFNEGLCOMPOSITORSETCONTEXTLISTEXTPROC glad_eglCompositorSetContextListEXT; #define eglCompositorSetContextListEXT glad_eglCompositorSetContextListEXT typedef EGLBoolean (APIENTRYP PFNEGLCOMPOSITORSETCONTEXTATTRIBUTESEXTPROC)(EGLint external_ref_id, const EGLint *context_attributes, EGLint num_entries); GLAPI PFNEGLCOMPOSITORSETCONTEXTATTRIBUTESEXTPROC glad_eglCompositorSetContextAttributesEXT; #define eglCompositorSetContextAttributesEXT glad_eglCompositorSetContextAttributesEXT typedef EGLBoolean (APIENTRYP PFNEGLCOMPOSITORSETWINDOWLISTEXTPROC)(EGLint external_ref_id, const EGLint *external_win_ids, EGLint num_entries); GLAPI PFNEGLCOMPOSITORSETWINDOWLISTEXTPROC glad_eglCompositorSetWindowListEXT; #define eglCompositorSetWindowListEXT glad_eglCompositorSetWindowListEXT typedef EGLBoolean (APIENTRYP PFNEGLCOMPOSITORSETWINDOWATTRIBUTESEXTPROC)(EGLint external_win_id, const EGLint *window_attributes, EGLint num_entries); GLAPI PFNEGLCOMPOSITORSETWINDOWATTRIBUTESEXTPROC glad_eglCompositorSetWindowAttributesEXT; #define eglCompositorSetWindowAttributesEXT glad_eglCompositorSetWindowAttributesEXT typedef EGLBoolean (APIENTRYP PFNEGLCOMPOSITORBINDTEXWINDOWEXTPROC)(EGLint external_win_id); GLAPI PFNEGLCOMPOSITORBINDTEXWINDOWEXTPROC glad_eglCompositorBindTexWindowEXT; #define eglCompositorBindTexWindowEXT glad_eglCompositorBindTexWindowEXT typedef EGLBoolean (APIENTRYP PFNEGLCOMPOSITORSETSIZEEXTPROC)(EGLint external_win_id, EGLint width, EGLint height); GLAPI PFNEGLCOMPOSITORSETSIZEEXTPROC glad_eglCompositorSetSizeEXT; #define eglCompositorSetSizeEXT glad_eglCompositorSetSizeEXT typedef EGLBoolean (APIENTRYP PFNEGLCOMPOSITORSWAPPOLICYEXTPROC)(EGLint external_win_id, EGLint policy); GLAPI PFNEGLCOMPOSITORSWAPPOLICYEXTPROC glad_eglCompositorSwapPolicyEXT; #define eglCompositorSwapPolicyEXT glad_eglCompositorSwapPolicyEXT #endif #ifndef EGL_EXT_create_context_robustness #define EGL_EXT_create_context_robustness 1 #endif #ifndef EGL_EXT_device_base #define EGL_EXT_device_base 1 typedef EGLBoolean (APIENTRYP PFNEGLQUERYDEVICEATTRIBEXTPROC)(EGLDeviceEXT device, EGLint attribute, EGLAttrib *value); GLAPI PFNEGLQUERYDEVICEATTRIBEXTPROC glad_eglQueryDeviceAttribEXT; #define eglQueryDeviceAttribEXT glad_eglQueryDeviceAttribEXT typedef const char * (APIENTRYP PFNEGLQUERYDEVICESTRINGEXTPROC)(EGLDeviceEXT device, EGLint name); GLAPI PFNEGLQUERYDEVICESTRINGEXTPROC glad_eglQueryDeviceStringEXT; #define eglQueryDeviceStringEXT glad_eglQueryDeviceStringEXT typedef EGLBoolean (APIENTRYP PFNEGLQUERYDEVICESEXTPROC)(EGLint max_devices, EGLDeviceEXT *devices, EGLint *num_devices); GLAPI PFNEGLQUERYDEVICESEXTPROC glad_eglQueryDevicesEXT; #define eglQueryDevicesEXT glad_eglQueryDevicesEXT typedef EGLBoolean (APIENTRYP PFNEGLQUERYDISPLAYATTRIBEXTPROC)(EGLDisplay dpy, EGLint attribute, EGLAttrib *value); GLAPI PFNEGLQUERYDISPLAYATTRIBEXTPROC glad_eglQueryDisplayAttribEXT; #define eglQueryDisplayAttribEXT glad_eglQueryDisplayAttribEXT #endif #ifndef EGL_EXT_device_drm #define EGL_EXT_device_drm 1 #endif #ifndef EGL_EXT_device_enumeration #define EGL_EXT_device_enumeration 1 #endif #ifndef EGL_EXT_device_openwf #define EGL_EXT_device_openwf 1 #endif #ifndef EGL_EXT_device_query #define EGL_EXT_device_query 1 #endif #ifndef EGL_EXT_gl_colorspace_bt2020_linear #define EGL_EXT_gl_colorspace_bt2020_linear 1 #endif #ifndef EGL_EXT_gl_colorspace_bt2020_pq #define EGL_EXT_gl_colorspace_bt2020_pq 1 #endif #ifndef EGL_EXT_gl_colorspace_display_p3 #define EGL_EXT_gl_colorspace_display_p3 1 #endif #ifndef EGL_EXT_gl_colorspace_display_p3_linear #define EGL_EXT_gl_colorspace_display_p3_linear 1 #endif #ifndef EGL_EXT_gl_colorspace_display_p3_passthrough #define EGL_EXT_gl_colorspace_display_p3_passthrough 1 #endif #ifndef EGL_EXT_gl_colorspace_scrgb #define EGL_EXT_gl_colorspace_scrgb 1 #endif #ifndef EGL_EXT_gl_colorspace_scrgb_linear #define EGL_EXT_gl_colorspace_scrgb_linear 1 #endif #ifndef EGL_EXT_image_dma_buf_import #define EGL_EXT_image_dma_buf_import 1 #endif #ifndef EGL_EXT_image_dma_buf_import_modifiers #define EGL_EXT_image_dma_buf_import_modifiers 1 typedef EGLBoolean (APIENTRYP PFNEGLQUERYDMABUFFORMATSEXTPROC)(EGLDisplay dpy, EGLint max_formats, EGLint *formats, EGLint *num_formats); GLAPI PFNEGLQUERYDMABUFFORMATSEXTPROC glad_eglQueryDmaBufFormatsEXT; #define eglQueryDmaBufFormatsEXT glad_eglQueryDmaBufFormatsEXT typedef EGLBoolean (APIENTRYP PFNEGLQUERYDMABUFMODIFIERSEXTPROC)(EGLDisplay dpy, EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers); GLAPI PFNEGLQUERYDMABUFMODIFIERSEXTPROC glad_eglQueryDmaBufModifiersEXT; #define eglQueryDmaBufModifiersEXT glad_eglQueryDmaBufModifiersEXT #endif #ifndef EGL_EXT_image_gl_colorspace #define EGL_EXT_image_gl_colorspace 1 #endif #ifndef EGL_EXT_image_implicit_sync_control #define EGL_EXT_image_implicit_sync_control 1 #endif #ifndef EGL_EXT_multiview_window #define EGL_EXT_multiview_window 1 #endif #ifndef EGL_EXT_output_base #define EGL_EXT_output_base 1 typedef EGLBoolean (APIENTRYP PFNEGLGETOUTPUTLAYERSEXTPROC)(EGLDisplay dpy, const EGLAttrib *attrib_list, EGLOutputLayerEXT *layers, EGLint max_layers, EGLint *num_layers); GLAPI PFNEGLGETOUTPUTLAYERSEXTPROC glad_eglGetOutputLayersEXT; #define eglGetOutputLayersEXT glad_eglGetOutputLayersEXT typedef EGLBoolean (APIENTRYP PFNEGLGETOUTPUTPORTSEXTPROC)(EGLDisplay dpy, const EGLAttrib *attrib_list, EGLOutputPortEXT *ports, EGLint max_ports, EGLint *num_ports); GLAPI PFNEGLGETOUTPUTPORTSEXTPROC glad_eglGetOutputPortsEXT; #define eglGetOutputPortsEXT glad_eglGetOutputPortsEXT typedef EGLBoolean (APIENTRYP PFNEGLOUTPUTLAYERATTRIBEXTPROC)(EGLDisplay dpy, EGLOutputLayerEXT layer, EGLint attribute, EGLAttrib value); GLAPI PFNEGLOUTPUTLAYERATTRIBEXTPROC glad_eglOutputLayerAttribEXT; #define eglOutputLayerAttribEXT glad_eglOutputLayerAttribEXT typedef EGLBoolean (APIENTRYP PFNEGLQUERYOUTPUTLAYERATTRIBEXTPROC)(EGLDisplay dpy, EGLOutputLayerEXT layer, EGLint attribute, EGLAttrib *value); GLAPI PFNEGLQUERYOUTPUTLAYERATTRIBEXTPROC glad_eglQueryOutputLayerAttribEXT; #define eglQueryOutputLayerAttribEXT glad_eglQueryOutputLayerAttribEXT typedef const char * (APIENTRYP PFNEGLQUERYOUTPUTLAYERSTRINGEXTPROC)(EGLDisplay dpy, EGLOutputLayerEXT layer, EGLint name); GLAPI PFNEGLQUERYOUTPUTLAYERSTRINGEXTPROC glad_eglQueryOutputLayerStringEXT; #define eglQueryOutputLayerStringEXT glad_eglQueryOutputLayerStringEXT typedef EGLBoolean (APIENTRYP PFNEGLOUTPUTPORTATTRIBEXTPROC)(EGLDisplay dpy, EGLOutputPortEXT port, EGLint attribute, EGLAttrib value); GLAPI PFNEGLOUTPUTPORTATTRIBEXTPROC glad_eglOutputPortAttribEXT; #define eglOutputPortAttribEXT glad_eglOutputPortAttribEXT typedef EGLBoolean (APIENTRYP PFNEGLQUERYOUTPUTPORTATTRIBEXTPROC)(EGLDisplay dpy, EGLOutputPortEXT port, EGLint attribute, EGLAttrib *value); GLAPI PFNEGLQUERYOUTPUTPORTATTRIBEXTPROC glad_eglQueryOutputPortAttribEXT; #define eglQueryOutputPortAttribEXT glad_eglQueryOutputPortAttribEXT typedef const char * (APIENTRYP PFNEGLQUERYOUTPUTPORTSTRINGEXTPROC)(EGLDisplay dpy, EGLOutputPortEXT port, EGLint name); GLAPI PFNEGLQUERYOUTPUTPORTSTRINGEXTPROC glad_eglQueryOutputPortStringEXT; #define eglQueryOutputPortStringEXT glad_eglQueryOutputPortStringEXT #endif #ifndef EGL_EXT_output_drm #define EGL_EXT_output_drm 1 #endif #ifndef EGL_EXT_output_openwf #define EGL_EXT_output_openwf 1 #endif #ifndef EGL_EXT_pixel_format_float #define EGL_EXT_pixel_format_float 1 #endif #ifndef EGL_EXT_platform_base #define EGL_EXT_platform_base 1 typedef EGLDisplay (APIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC)(EGLenum platform, void *native_display, const EGLint *attrib_list); GLAPI PFNEGLGETPLATFORMDISPLAYEXTPROC glad_eglGetPlatformDisplayEXT; #define eglGetPlatformDisplayEXT glad_eglGetPlatformDisplayEXT typedef EGLSurface (APIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)(EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list); GLAPI PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC glad_eglCreatePlatformWindowSurfaceEXT; #define eglCreatePlatformWindowSurfaceEXT glad_eglCreatePlatformWindowSurfaceEXT typedef EGLSurface (APIENTRYP PFNEGLCREATEPLATFORMPIXMAPSURFACEEXTPROC)(EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLint *attrib_list); GLAPI PFNEGLCREATEPLATFORMPIXMAPSURFACEEXTPROC glad_eglCreatePlatformPixmapSurfaceEXT; #define eglCreatePlatformPixmapSurfaceEXT glad_eglCreatePlatformPixmapSurfaceEXT #endif #ifndef EGL_EXT_platform_device #define EGL_EXT_platform_device 1 #endif #ifndef EGL_EXT_platform_wayland #define EGL_EXT_platform_wayland 1 #endif #ifndef EGL_EXT_platform_x11 #define EGL_EXT_platform_x11 1 #endif #ifndef EGL_EXT_protected_content #define EGL_EXT_protected_content 1 #endif #ifndef EGL_EXT_protected_surface #define EGL_EXT_protected_surface 1 #endif #ifndef EGL_EXT_stream_consumer_egloutput #define EGL_EXT_stream_consumer_egloutput 1 typedef EGLBoolean (APIENTRYP PFNEGLSTREAMCONSUMEROUTPUTEXTPROC)(EGLDisplay dpy, EGLStreamKHR stream, EGLOutputLayerEXT layer); GLAPI PFNEGLSTREAMCONSUMEROUTPUTEXTPROC glad_eglStreamConsumerOutputEXT; #define eglStreamConsumerOutputEXT glad_eglStreamConsumerOutputEXT #endif #ifndef EGL_EXT_surface_CTA861_3_metadata #define EGL_EXT_surface_CTA861_3_metadata 1 #endif #ifndef EGL_EXT_surface_SMPTE2086_metadata #define EGL_EXT_surface_SMPTE2086_metadata 1 #endif #ifndef EGL_EXT_swap_buffers_with_damage #define EGL_EXT_swap_buffers_with_damage 1 typedef EGLBoolean (APIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC)(EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects); GLAPI PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC glad_eglSwapBuffersWithDamageEXT; #define eglSwapBuffersWithDamageEXT glad_eglSwapBuffersWithDamageEXT #endif #ifndef EGL_EXT_sync_reuse #define EGL_EXT_sync_reuse 1 typedef EGLBoolean (APIENTRYP PFNEGLUNSIGNALSYNCEXTPROC)(EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list); GLAPI PFNEGLUNSIGNALSYNCEXTPROC glad_eglUnsignalSyncEXT; #define eglUnsignalSyncEXT glad_eglUnsignalSyncEXT #endif #ifndef EGL_EXT_yuv_surface #define EGL_EXT_yuv_surface 1 #endif #ifndef EGL_HI_clientpixmap #define EGL_HI_clientpixmap 1 typedef EGLSurface (APIENTRYP PFNEGLCREATEPIXMAPSURFACEHIPROC)(EGLDisplay dpy, EGLConfig config, struct EGLClientPixmapHI *pixmap); GLAPI PFNEGLCREATEPIXMAPSURFACEHIPROC glad_eglCreatePixmapSurfaceHI; #define eglCreatePixmapSurfaceHI glad_eglCreatePixmapSurfaceHI #endif #ifndef EGL_HI_colorformats #define EGL_HI_colorformats 1 #endif #ifndef EGL_IMG_context_priority #define EGL_IMG_context_priority 1 #endif #ifndef EGL_IMG_image_plane_attribs #define EGL_IMG_image_plane_attribs 1 #endif #ifndef EGL_KHR_cl_event #define EGL_KHR_cl_event 1 #endif #ifndef EGL_KHR_cl_event2 #define EGL_KHR_cl_event2 1 typedef EGLSyncKHR (APIENTRYP PFNEGLCREATESYNC64KHRPROC)(EGLDisplay dpy, EGLenum type, const EGLAttribKHR *attrib_list); GLAPI PFNEGLCREATESYNC64KHRPROC glad_eglCreateSync64KHR; #define eglCreateSync64KHR glad_eglCreateSync64KHR #endif #ifndef EGL_KHR_client_get_all_proc_addresses #define EGL_KHR_client_get_all_proc_addresses 1 #endif #ifndef EGL_KHR_config_attribs #define EGL_KHR_config_attribs 1 #endif #ifndef EGL_KHR_context_flush_control #define EGL_KHR_context_flush_control 1 #endif #ifndef EGL_KHR_create_context #define EGL_KHR_create_context 1 #endif #ifndef EGL_KHR_create_context_no_error #define EGL_KHR_create_context_no_error 1 #endif #ifndef EGL_KHR_debug #define EGL_KHR_debug 1 typedef EGLint (APIENTRYP PFNEGLDEBUGMESSAGECONTROLKHRPROC)(EGLDEBUGPROCKHR callback, const EGLAttrib *attrib_list); GLAPI PFNEGLDEBUGMESSAGECONTROLKHRPROC glad_eglDebugMessageControlKHR; #define eglDebugMessageControlKHR glad_eglDebugMessageControlKHR typedef EGLBoolean (APIENTRYP PFNEGLQUERYDEBUGKHRPROC)(EGLint attribute, EGLAttrib *value); GLAPI PFNEGLQUERYDEBUGKHRPROC glad_eglQueryDebugKHR; #define eglQueryDebugKHR glad_eglQueryDebugKHR typedef EGLint (APIENTRYP PFNEGLLABELOBJECTKHRPROC)(EGLDisplay display, EGLenum objectType, EGLObjectKHR object, EGLLabelKHR label); GLAPI PFNEGLLABELOBJECTKHRPROC glad_eglLabelObjectKHR; #define eglLabelObjectKHR glad_eglLabelObjectKHR #endif #ifndef EGL_KHR_display_reference #define EGL_KHR_display_reference 1 typedef EGLBoolean (APIENTRYP PFNEGLQUERYDISPLAYATTRIBKHRPROC)(EGLDisplay dpy, EGLint name, EGLAttrib *value); GLAPI PFNEGLQUERYDISPLAYATTRIBKHRPROC glad_eglQueryDisplayAttribKHR; #define eglQueryDisplayAttribKHR glad_eglQueryDisplayAttribKHR #endif #ifndef EGL_KHR_fence_sync #define EGL_KHR_fence_sync 1 typedef EGLSyncKHR (APIENTRYP PFNEGLCREATESYNCKHRPROC)(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list); GLAPI PFNEGLCREATESYNCKHRPROC glad_eglCreateSyncKHR; #define eglCreateSyncKHR glad_eglCreateSyncKHR typedef EGLBoolean (APIENTRYP PFNEGLDESTROYSYNCKHRPROC)(EGLDisplay dpy, EGLSyncKHR sync); GLAPI PFNEGLDESTROYSYNCKHRPROC glad_eglDestroySyncKHR; #define eglDestroySyncKHR glad_eglDestroySyncKHR typedef EGLint (APIENTRYP PFNEGLCLIENTWAITSYNCKHRPROC)(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout); GLAPI PFNEGLCLIENTWAITSYNCKHRPROC glad_eglClientWaitSyncKHR; #define eglClientWaitSyncKHR glad_eglClientWaitSyncKHR typedef EGLBoolean (APIENTRYP PFNEGLGETSYNCATTRIBKHRPROC)(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value); GLAPI PFNEGLGETSYNCATTRIBKHRPROC glad_eglGetSyncAttribKHR; #define eglGetSyncAttribKHR glad_eglGetSyncAttribKHR #endif #ifndef EGL_KHR_get_all_proc_addresses #define EGL_KHR_get_all_proc_addresses 1 #endif #ifndef EGL_KHR_gl_colorspace #define EGL_KHR_gl_colorspace 1 #endif #ifndef EGL_KHR_gl_renderbuffer_image #define EGL_KHR_gl_renderbuffer_image 1 #endif #ifndef EGL_KHR_gl_texture_2D_image #define EGL_KHR_gl_texture_2D_image 1 #endif #ifndef EGL_KHR_gl_texture_3D_image #define EGL_KHR_gl_texture_3D_image 1 #endif #ifndef EGL_KHR_gl_texture_cubemap_image #define EGL_KHR_gl_texture_cubemap_image 1 #endif #ifndef EGL_KHR_image #define EGL_KHR_image 1 typedef EGLImageKHR (APIENTRYP PFNEGLCREATEIMAGEKHRPROC)(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); GLAPI PFNEGLCREATEIMAGEKHRPROC glad_eglCreateImageKHR; #define eglCreateImageKHR glad_eglCreateImageKHR typedef EGLBoolean (APIENTRYP PFNEGLDESTROYIMAGEKHRPROC)(EGLDisplay dpy, EGLImageKHR image); GLAPI PFNEGLDESTROYIMAGEKHRPROC glad_eglDestroyImageKHR; #define eglDestroyImageKHR glad_eglDestroyImageKHR #endif #ifndef EGL_KHR_image_base #define EGL_KHR_image_base 1 #endif #ifndef EGL_KHR_image_pixmap #define EGL_KHR_image_pixmap 1 #endif #ifndef EGL_KHR_lock_surface #define EGL_KHR_lock_surface 1 typedef EGLBoolean (APIENTRYP PFNEGLLOCKSURFACEKHRPROC)(EGLDisplay dpy, EGLSurface surface, const EGLint *attrib_list); GLAPI PFNEGLLOCKSURFACEKHRPROC glad_eglLockSurfaceKHR; #define eglLockSurfaceKHR glad_eglLockSurfaceKHR typedef EGLBoolean (APIENTRYP PFNEGLUNLOCKSURFACEKHRPROC)(EGLDisplay dpy, EGLSurface surface); GLAPI PFNEGLUNLOCKSURFACEKHRPROC glad_eglUnlockSurfaceKHR; #define eglUnlockSurfaceKHR glad_eglUnlockSurfaceKHR #endif #ifndef EGL_KHR_lock_surface2 #define EGL_KHR_lock_surface2 1 #endif #ifndef EGL_KHR_lock_surface3 #define EGL_KHR_lock_surface3 1 typedef EGLBoolean (APIENTRYP PFNEGLQUERYSURFACE64KHRPROC)(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLAttribKHR *value); GLAPI PFNEGLQUERYSURFACE64KHRPROC glad_eglQuerySurface64KHR; #define eglQuerySurface64KHR glad_eglQuerySurface64KHR #endif #ifndef EGL_KHR_mutable_render_buffer #define EGL_KHR_mutable_render_buffer 1 #endif #ifndef EGL_KHR_no_config_context #define EGL_KHR_no_config_context 1 #endif #ifndef EGL_KHR_partial_update #define EGL_KHR_partial_update 1 typedef EGLBoolean (APIENTRYP PFNEGLSETDAMAGEREGIONKHRPROC)(EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects); GLAPI PFNEGLSETDAMAGEREGIONKHRPROC glad_eglSetDamageRegionKHR; #define eglSetDamageRegionKHR glad_eglSetDamageRegionKHR #endif #ifndef EGL_KHR_platform_android #define EGL_KHR_platform_android 1 #endif #ifndef EGL_KHR_platform_gbm #define EGL_KHR_platform_gbm 1 #endif #ifndef EGL_KHR_platform_wayland #define EGL_KHR_platform_wayland 1 #endif #ifndef EGL_KHR_platform_x11 #define EGL_KHR_platform_x11 1 #endif #ifndef EGL_KHR_reusable_sync #define EGL_KHR_reusable_sync 1 typedef EGLBoolean (APIENTRYP PFNEGLSIGNALSYNCKHRPROC)(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode); GLAPI PFNEGLSIGNALSYNCKHRPROC glad_eglSignalSyncKHR; #define eglSignalSyncKHR glad_eglSignalSyncKHR #endif #ifndef EGL_KHR_stream #define EGL_KHR_stream 1 typedef EGLStreamKHR (APIENTRYP PFNEGLCREATESTREAMKHRPROC)(EGLDisplay dpy, const EGLint *attrib_list); GLAPI PFNEGLCREATESTREAMKHRPROC glad_eglCreateStreamKHR; #define eglCreateStreamKHR glad_eglCreateStreamKHR typedef EGLBoolean (APIENTRYP PFNEGLDESTROYSTREAMKHRPROC)(EGLDisplay dpy, EGLStreamKHR stream); GLAPI PFNEGLDESTROYSTREAMKHRPROC glad_eglDestroyStreamKHR; #define eglDestroyStreamKHR glad_eglDestroyStreamKHR typedef EGLBoolean (APIENTRYP PFNEGLSTREAMATTRIBKHRPROC)(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLint value); GLAPI PFNEGLSTREAMATTRIBKHRPROC glad_eglStreamAttribKHR; #define eglStreamAttribKHR glad_eglStreamAttribKHR typedef EGLBoolean (APIENTRYP PFNEGLQUERYSTREAMKHRPROC)(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLint *value); GLAPI PFNEGLQUERYSTREAMKHRPROC glad_eglQueryStreamKHR; #define eglQueryStreamKHR glad_eglQueryStreamKHR typedef EGLBoolean (APIENTRYP PFNEGLQUERYSTREAMU64KHRPROC)(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLuint64KHR *value); GLAPI PFNEGLQUERYSTREAMU64KHRPROC glad_eglQueryStreamu64KHR; #define eglQueryStreamu64KHR glad_eglQueryStreamu64KHR #endif #ifndef EGL_KHR_stream_attrib #define EGL_KHR_stream_attrib 1 typedef EGLStreamKHR (APIENTRYP PFNEGLCREATESTREAMATTRIBKHRPROC)(EGLDisplay dpy, const EGLAttrib *attrib_list); GLAPI PFNEGLCREATESTREAMATTRIBKHRPROC glad_eglCreateStreamAttribKHR; #define eglCreateStreamAttribKHR glad_eglCreateStreamAttribKHR typedef EGLBoolean (APIENTRYP PFNEGLSETSTREAMATTRIBKHRPROC)(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLAttrib value); GLAPI PFNEGLSETSTREAMATTRIBKHRPROC glad_eglSetStreamAttribKHR; #define eglSetStreamAttribKHR glad_eglSetStreamAttribKHR typedef EGLBoolean (APIENTRYP PFNEGLQUERYSTREAMATTRIBKHRPROC)(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLAttrib *value); GLAPI PFNEGLQUERYSTREAMATTRIBKHRPROC glad_eglQueryStreamAttribKHR; #define eglQueryStreamAttribKHR glad_eglQueryStreamAttribKHR typedef EGLBoolean (APIENTRYP PFNEGLSTREAMCONSUMERACQUIREATTRIBKHRPROC)(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); GLAPI PFNEGLSTREAMCONSUMERACQUIREATTRIBKHRPROC glad_eglStreamConsumerAcquireAttribKHR; #define eglStreamConsumerAcquireAttribKHR glad_eglStreamConsumerAcquireAttribKHR typedef EGLBoolean (APIENTRYP PFNEGLSTREAMCONSUMERRELEASEATTRIBKHRPROC)(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); GLAPI PFNEGLSTREAMCONSUMERRELEASEATTRIBKHRPROC glad_eglStreamConsumerReleaseAttribKHR; #define eglStreamConsumerReleaseAttribKHR glad_eglStreamConsumerReleaseAttribKHR #endif #ifndef EGL_KHR_stream_consumer_gltexture #define EGL_KHR_stream_consumer_gltexture 1 typedef EGLBoolean (APIENTRYP PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHRPROC)(EGLDisplay dpy, EGLStreamKHR stream); GLAPI PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHRPROC glad_eglStreamConsumerGLTextureExternalKHR; #define eglStreamConsumerGLTextureExternalKHR glad_eglStreamConsumerGLTextureExternalKHR typedef EGLBoolean (APIENTRYP PFNEGLSTREAMCONSUMERACQUIREKHRPROC)(EGLDisplay dpy, EGLStreamKHR stream); GLAPI PFNEGLSTREAMCONSUMERACQUIREKHRPROC glad_eglStreamConsumerAcquireKHR; #define eglStreamConsumerAcquireKHR glad_eglStreamConsumerAcquireKHR typedef EGLBoolean (APIENTRYP PFNEGLSTREAMCONSUMERRELEASEKHRPROC)(EGLDisplay dpy, EGLStreamKHR stream); GLAPI PFNEGLSTREAMCONSUMERRELEASEKHRPROC glad_eglStreamConsumerReleaseKHR; #define eglStreamConsumerReleaseKHR glad_eglStreamConsumerReleaseKHR #endif #ifndef EGL_KHR_stream_cross_process_fd #define EGL_KHR_stream_cross_process_fd 1 typedef EGLNativeFileDescriptorKHR (APIENTRYP PFNEGLGETSTREAMFILEDESCRIPTORKHRPROC)(EGLDisplay dpy, EGLStreamKHR stream); GLAPI PFNEGLGETSTREAMFILEDESCRIPTORKHRPROC glad_eglGetStreamFileDescriptorKHR; #define eglGetStreamFileDescriptorKHR glad_eglGetStreamFileDescriptorKHR typedef EGLStreamKHR (APIENTRYP PFNEGLCREATESTREAMFROMFILEDESCRIPTORKHRPROC)(EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor); GLAPI PFNEGLCREATESTREAMFROMFILEDESCRIPTORKHRPROC glad_eglCreateStreamFromFileDescriptorKHR; #define eglCreateStreamFromFileDescriptorKHR glad_eglCreateStreamFromFileDescriptorKHR #endif #ifndef EGL_KHR_stream_fifo #define EGL_KHR_stream_fifo 1 typedef EGLBoolean (APIENTRYP PFNEGLQUERYSTREAMTIMEKHRPROC)(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLTimeKHR *value); GLAPI PFNEGLQUERYSTREAMTIMEKHRPROC glad_eglQueryStreamTimeKHR; #define eglQueryStreamTimeKHR glad_eglQueryStreamTimeKHR #endif #ifndef EGL_KHR_stream_producer_aldatalocator #define EGL_KHR_stream_producer_aldatalocator 1 #endif #ifndef EGL_KHR_stream_producer_eglsurface #define EGL_KHR_stream_producer_eglsurface 1 typedef EGLSurface (APIENTRYP PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC)(EGLDisplay dpy, EGLConfig config, EGLStreamKHR stream, const EGLint *attrib_list); GLAPI PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC glad_eglCreateStreamProducerSurfaceKHR; #define eglCreateStreamProducerSurfaceKHR glad_eglCreateStreamProducerSurfaceKHR #endif #ifndef EGL_KHR_surfaceless_context #define EGL_KHR_surfaceless_context 1 #endif #ifndef EGL_KHR_swap_buffers_with_damage #define EGL_KHR_swap_buffers_with_damage 1 typedef EGLBoolean (APIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC)(EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects); GLAPI PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC glad_eglSwapBuffersWithDamageKHR; #define eglSwapBuffersWithDamageKHR glad_eglSwapBuffersWithDamageKHR #endif #ifndef EGL_KHR_vg_parent_image #define EGL_KHR_vg_parent_image 1 #endif #ifndef EGL_KHR_wait_sync #define EGL_KHR_wait_sync 1 typedef EGLint (APIENTRYP PFNEGLWAITSYNCKHRPROC)(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags); GLAPI PFNEGLWAITSYNCKHRPROC glad_eglWaitSyncKHR; #define eglWaitSyncKHR glad_eglWaitSyncKHR #endif #ifndef EGL_MESA_drm_image #define EGL_MESA_drm_image 1 typedef EGLImageKHR (APIENTRYP PFNEGLCREATEDRMIMAGEMESAPROC)(EGLDisplay dpy, const EGLint *attrib_list); GLAPI PFNEGLCREATEDRMIMAGEMESAPROC glad_eglCreateDRMImageMESA; #define eglCreateDRMImageMESA glad_eglCreateDRMImageMESA typedef EGLBoolean (APIENTRYP PFNEGLEXPORTDRMIMAGEMESAPROC)(EGLDisplay dpy, EGLImageKHR image, EGLint *name, EGLint *handle, EGLint *stride); GLAPI PFNEGLEXPORTDRMIMAGEMESAPROC glad_eglExportDRMImageMESA; #define eglExportDRMImageMESA glad_eglExportDRMImageMESA #endif #ifndef EGL_MESA_image_dma_buf_export #define EGL_MESA_image_dma_buf_export 1 typedef EGLBoolean (APIENTRYP PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC)(EGLDisplay dpy, EGLImageKHR image, int *fourcc, int *num_planes, EGLuint64KHR *modifiers); GLAPI PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC glad_eglExportDMABUFImageQueryMESA; #define eglExportDMABUFImageQueryMESA glad_eglExportDMABUFImageQueryMESA typedef EGLBoolean (APIENTRYP PFNEGLEXPORTDMABUFIMAGEMESAPROC)(EGLDisplay dpy, EGLImageKHR image, int *fds, EGLint *strides, EGLint *offsets); GLAPI PFNEGLEXPORTDMABUFIMAGEMESAPROC glad_eglExportDMABUFImageMESA; #define eglExportDMABUFImageMESA glad_eglExportDMABUFImageMESA #endif #ifndef EGL_MESA_platform_gbm #define EGL_MESA_platform_gbm 1 #endif #ifndef EGL_MESA_platform_surfaceless #define EGL_MESA_platform_surfaceless 1 #endif #ifndef EGL_NOK_swap_region #define EGL_NOK_swap_region 1 typedef EGLBoolean (APIENTRYP PFNEGLSWAPBUFFERSREGIONNOKPROC)(EGLDisplay dpy, EGLSurface surface, EGLint numRects, const EGLint *rects); GLAPI PFNEGLSWAPBUFFERSREGIONNOKPROC glad_eglSwapBuffersRegionNOK; #define eglSwapBuffersRegionNOK glad_eglSwapBuffersRegionNOK #endif #ifndef EGL_NOK_swap_region2 #define EGL_NOK_swap_region2 1 typedef EGLBoolean (APIENTRYP PFNEGLSWAPBUFFERSREGION2NOKPROC)(EGLDisplay dpy, EGLSurface surface, EGLint numRects, const EGLint *rects); GLAPI PFNEGLSWAPBUFFERSREGION2NOKPROC glad_eglSwapBuffersRegion2NOK; #define eglSwapBuffersRegion2NOK glad_eglSwapBuffersRegion2NOK #endif #ifndef EGL_NOK_texture_from_pixmap #define EGL_NOK_texture_from_pixmap 1 #endif #ifndef EGL_NV_3dvision_surface #define EGL_NV_3dvision_surface 1 #endif #ifndef EGL_NV_context_priority_realtime #define EGL_NV_context_priority_realtime 1 #endif #ifndef EGL_NV_coverage_sample #define EGL_NV_coverage_sample 1 #endif #ifndef EGL_NV_coverage_sample_resolve #define EGL_NV_coverage_sample_resolve 1 #endif #ifndef EGL_NV_cuda_event #define EGL_NV_cuda_event 1 #endif #ifndef EGL_NV_depth_nonlinear #define EGL_NV_depth_nonlinear 1 #endif #ifndef EGL_NV_device_cuda #define EGL_NV_device_cuda 1 #endif #ifndef EGL_NV_native_query #define EGL_NV_native_query 1 typedef EGLBoolean (APIENTRYP PFNEGLQUERYNATIVEDISPLAYNVPROC)(EGLDisplay dpy, EGLNativeDisplayType *display_id); GLAPI PFNEGLQUERYNATIVEDISPLAYNVPROC glad_eglQueryNativeDisplayNV; #define eglQueryNativeDisplayNV glad_eglQueryNativeDisplayNV typedef EGLBoolean (APIENTRYP PFNEGLQUERYNATIVEWINDOWNVPROC)(EGLDisplay dpy, EGLSurface surf, EGLNativeWindowType *window); GLAPI PFNEGLQUERYNATIVEWINDOWNVPROC glad_eglQueryNativeWindowNV; #define eglQueryNativeWindowNV glad_eglQueryNativeWindowNV typedef EGLBoolean (APIENTRYP PFNEGLQUERYNATIVEPIXMAPNVPROC)(EGLDisplay dpy, EGLSurface surf, EGLNativePixmapType *pixmap); GLAPI PFNEGLQUERYNATIVEPIXMAPNVPROC glad_eglQueryNativePixmapNV; #define eglQueryNativePixmapNV glad_eglQueryNativePixmapNV #endif #ifndef EGL_NV_post_convert_rounding #define EGL_NV_post_convert_rounding 1 #endif #ifndef EGL_NV_post_sub_buffer #define EGL_NV_post_sub_buffer 1 typedef EGLBoolean (APIENTRYP PFNEGLPOSTSUBBUFFERNVPROC)(EGLDisplay dpy, EGLSurface surface, EGLint x, EGLint y, EGLint width, EGLint height); GLAPI PFNEGLPOSTSUBBUFFERNVPROC glad_eglPostSubBufferNV; #define eglPostSubBufferNV glad_eglPostSubBufferNV #endif #ifndef EGL_NV_robustness_video_memory_purge #define EGL_NV_robustness_video_memory_purge 1 #endif #ifndef EGL_NV_stream_consumer_gltexture_yuv #define EGL_NV_stream_consumer_gltexture_yuv 1 typedef EGLBoolean (APIENTRYP PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALATTRIBSNVPROC)(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); GLAPI PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALATTRIBSNVPROC glad_eglStreamConsumerGLTextureExternalAttribsNV; #define eglStreamConsumerGLTextureExternalAttribsNV glad_eglStreamConsumerGLTextureExternalAttribsNV #endif #ifndef EGL_NV_stream_cross_display #define EGL_NV_stream_cross_display 1 #endif #ifndef EGL_NV_stream_cross_object #define EGL_NV_stream_cross_object 1 #endif #ifndef EGL_NV_stream_cross_partition #define EGL_NV_stream_cross_partition 1 #endif #ifndef EGL_NV_stream_cross_process #define EGL_NV_stream_cross_process 1 #endif #ifndef EGL_NV_stream_cross_system #define EGL_NV_stream_cross_system 1 #endif #ifndef EGL_NV_stream_fifo_next #define EGL_NV_stream_fifo_next 1 #endif #ifndef EGL_NV_stream_fifo_synchronous #define EGL_NV_stream_fifo_synchronous 1 #endif #ifndef EGL_NV_stream_flush #define EGL_NV_stream_flush 1 typedef EGLBoolean (APIENTRYP PFNEGLSTREAMFLUSHNVPROC)(EGLDisplay dpy, EGLStreamKHR stream); GLAPI PFNEGLSTREAMFLUSHNVPROC glad_eglStreamFlushNV; #define eglStreamFlushNV glad_eglStreamFlushNV #endif #ifndef EGL_NV_stream_frame_limits #define EGL_NV_stream_frame_limits 1 #endif #ifndef EGL_NV_stream_metadata #define EGL_NV_stream_metadata 1 typedef EGLBoolean (APIENTRYP PFNEGLQUERYDISPLAYATTRIBNVPROC)(EGLDisplay dpy, EGLint attribute, EGLAttrib *value); GLAPI PFNEGLQUERYDISPLAYATTRIBNVPROC glad_eglQueryDisplayAttribNV; #define eglQueryDisplayAttribNV glad_eglQueryDisplayAttribNV typedef EGLBoolean (APIENTRYP PFNEGLSETSTREAMMETADATANVPROC)(EGLDisplay dpy, EGLStreamKHR stream, EGLint n, EGLint offset, EGLint size, const void *data); GLAPI PFNEGLSETSTREAMMETADATANVPROC glad_eglSetStreamMetadataNV; #define eglSetStreamMetadataNV glad_eglSetStreamMetadataNV typedef EGLBoolean (APIENTRYP PFNEGLQUERYSTREAMMETADATANVPROC)(EGLDisplay dpy, EGLStreamKHR stream, EGLenum name, EGLint n, EGLint offset, EGLint size, void *data); GLAPI PFNEGLQUERYSTREAMMETADATANVPROC glad_eglQueryStreamMetadataNV; #define eglQueryStreamMetadataNV glad_eglQueryStreamMetadataNV #endif #ifndef EGL_NV_stream_remote #define EGL_NV_stream_remote 1 #endif #ifndef EGL_NV_stream_reset #define EGL_NV_stream_reset 1 typedef EGLBoolean (APIENTRYP PFNEGLRESETSTREAMNVPROC)(EGLDisplay dpy, EGLStreamKHR stream); GLAPI PFNEGLRESETSTREAMNVPROC glad_eglResetStreamNV; #define eglResetStreamNV glad_eglResetStreamNV #endif #ifndef EGL_NV_stream_socket #define EGL_NV_stream_socket 1 #endif #ifndef EGL_NV_stream_socket_inet #define EGL_NV_stream_socket_inet 1 #endif #ifndef EGL_NV_stream_socket_unix #define EGL_NV_stream_socket_unix 1 #endif #ifndef EGL_NV_stream_sync #define EGL_NV_stream_sync 1 typedef EGLSyncKHR (APIENTRYP PFNEGLCREATESTREAMSYNCNVPROC)(EGLDisplay dpy, EGLStreamKHR stream, EGLenum type, const EGLint *attrib_list); GLAPI PFNEGLCREATESTREAMSYNCNVPROC glad_eglCreateStreamSyncNV; #define eglCreateStreamSyncNV glad_eglCreateStreamSyncNV #endif #ifndef EGL_NV_sync #define EGL_NV_sync 1 typedef EGLSyncNV (APIENTRYP PFNEGLCREATEFENCESYNCNVPROC)(EGLDisplay dpy, EGLenum condition, const EGLint *attrib_list); GLAPI PFNEGLCREATEFENCESYNCNVPROC glad_eglCreateFenceSyncNV; #define eglCreateFenceSyncNV glad_eglCreateFenceSyncNV typedef EGLBoolean (APIENTRYP PFNEGLDESTROYSYNCNVPROC)(EGLSyncNV sync); GLAPI PFNEGLDESTROYSYNCNVPROC glad_eglDestroySyncNV; #define eglDestroySyncNV glad_eglDestroySyncNV typedef EGLBoolean (APIENTRYP PFNEGLFENCENVPROC)(EGLSyncNV sync); GLAPI PFNEGLFENCENVPROC glad_eglFenceNV; #define eglFenceNV glad_eglFenceNV typedef EGLint (APIENTRYP PFNEGLCLIENTWAITSYNCNVPROC)(EGLSyncNV sync, EGLint flags, EGLTimeNV timeout); GLAPI PFNEGLCLIENTWAITSYNCNVPROC glad_eglClientWaitSyncNV; #define eglClientWaitSyncNV glad_eglClientWaitSyncNV typedef EGLBoolean (APIENTRYP PFNEGLSIGNALSYNCNVPROC)(EGLSyncNV sync, EGLenum mode); GLAPI PFNEGLSIGNALSYNCNVPROC glad_eglSignalSyncNV; #define eglSignalSyncNV glad_eglSignalSyncNV typedef EGLBoolean (APIENTRYP PFNEGLGETSYNCATTRIBNVPROC)(EGLSyncNV sync, EGLint attribute, EGLint *value); GLAPI PFNEGLGETSYNCATTRIBNVPROC glad_eglGetSyncAttribNV; #define eglGetSyncAttribNV glad_eglGetSyncAttribNV #endif #ifndef EGL_NV_system_time #define EGL_NV_system_time 1 typedef EGLuint64NV (APIENTRYP PFNEGLGETSYSTEMTIMEFREQUENCYNVPROC)(void); GLAPI PFNEGLGETSYSTEMTIMEFREQUENCYNVPROC glad_eglGetSystemTimeFrequencyNV; #define eglGetSystemTimeFrequencyNV glad_eglGetSystemTimeFrequencyNV typedef EGLuint64NV (APIENTRYP PFNEGLGETSYSTEMTIMENVPROC)(void); GLAPI PFNEGLGETSYSTEMTIMENVPROC glad_eglGetSystemTimeNV; #define eglGetSystemTimeNV glad_eglGetSystemTimeNV #endif #ifndef EGL_TIZEN_image_native_buffer #define EGL_TIZEN_image_native_buffer 1 #endif #ifndef EGL_TIZEN_image_native_surface #define EGL_TIZEN_image_native_surface 1 #endif #ifdef __cplusplus } #endif #endif ================================================ FILE: src/3rdparty/glad/src/glad_egl.c ================================================ /* EGL loader generated by glad 0.1.25 on Mon Jan 14 16:56:21 2019. Language/Generator: C/C++ Specification: egl APIs: egl=1.5 Profile: - Extensions: EGL_ANDROID_blob_cache, EGL_ANDROID_create_native_client_buffer, EGL_ANDROID_framebuffer_target, EGL_ANDROID_front_buffer_auto_refresh, EGL_ANDROID_get_frame_timestamps, EGL_ANDROID_get_native_client_buffer, EGL_ANDROID_image_native_buffer, EGL_ANDROID_native_fence_sync, EGL_ANDROID_presentation_time, EGL_ANDROID_recordable, EGL_ANGLE_d3d_share_handle_client_buffer, EGL_ANGLE_device_d3d, EGL_ANGLE_query_surface_pointer, EGL_ANGLE_surface_d3d_texture_2d_share_handle, EGL_ANGLE_window_fixed_size, EGL_ARM_implicit_external_sync, EGL_ARM_pixmap_multisample_discard, EGL_EXT_bind_to_front, EGL_EXT_buffer_age, EGL_EXT_client_extensions, EGL_EXT_client_sync, EGL_EXT_compositor, EGL_EXT_create_context_robustness, EGL_EXT_device_base, EGL_EXT_device_drm, EGL_EXT_device_enumeration, EGL_EXT_device_openwf, EGL_EXT_device_query, EGL_EXT_gl_colorspace_bt2020_linear, EGL_EXT_gl_colorspace_bt2020_pq, EGL_EXT_gl_colorspace_display_p3, EGL_EXT_gl_colorspace_display_p3_linear, EGL_EXT_gl_colorspace_display_p3_passthrough, EGL_EXT_gl_colorspace_scrgb, EGL_EXT_gl_colorspace_scrgb_linear, EGL_EXT_image_dma_buf_import, EGL_EXT_image_dma_buf_import_modifiers, EGL_EXT_image_gl_colorspace, EGL_EXT_image_implicit_sync_control, EGL_EXT_multiview_window, EGL_EXT_output_base, EGL_EXT_output_drm, EGL_EXT_output_openwf, EGL_EXT_pixel_format_float, EGL_EXT_platform_base, EGL_EXT_platform_device, EGL_EXT_platform_wayland, EGL_EXT_platform_x11, EGL_EXT_protected_content, EGL_EXT_protected_surface, EGL_EXT_stream_consumer_egloutput, EGL_EXT_surface_CTA861_3_metadata, EGL_EXT_surface_SMPTE2086_metadata, EGL_EXT_swap_buffers_with_damage, EGL_EXT_sync_reuse, EGL_EXT_yuv_surface, EGL_HI_clientpixmap, EGL_HI_colorformats, EGL_IMG_context_priority, EGL_IMG_image_plane_attribs, EGL_KHR_cl_event, EGL_KHR_cl_event2, EGL_KHR_client_get_all_proc_addresses, EGL_KHR_config_attribs, EGL_KHR_context_flush_control, EGL_KHR_create_context, EGL_KHR_create_context_no_error, EGL_KHR_debug, EGL_KHR_display_reference, EGL_KHR_fence_sync, EGL_KHR_get_all_proc_addresses, EGL_KHR_gl_colorspace, EGL_KHR_gl_renderbuffer_image, EGL_KHR_gl_texture_2D_image, EGL_KHR_gl_texture_3D_image, EGL_KHR_gl_texture_cubemap_image, EGL_KHR_image, EGL_KHR_image_base, EGL_KHR_image_pixmap, EGL_KHR_lock_surface, EGL_KHR_lock_surface2, EGL_KHR_lock_surface3, EGL_KHR_mutable_render_buffer, EGL_KHR_no_config_context, EGL_KHR_partial_update, EGL_KHR_platform_android, EGL_KHR_platform_gbm, EGL_KHR_platform_wayland, EGL_KHR_platform_x11, EGL_KHR_reusable_sync, EGL_KHR_stream, EGL_KHR_stream_attrib, EGL_KHR_stream_consumer_gltexture, EGL_KHR_stream_cross_process_fd, EGL_KHR_stream_fifo, EGL_KHR_stream_producer_aldatalocator, EGL_KHR_stream_producer_eglsurface, EGL_KHR_surfaceless_context, EGL_KHR_swap_buffers_with_damage, EGL_KHR_vg_parent_image, EGL_KHR_wait_sync, EGL_MESA_drm_image, EGL_MESA_image_dma_buf_export, EGL_MESA_platform_gbm, EGL_MESA_platform_surfaceless, EGL_NOK_swap_region, EGL_NOK_swap_region2, EGL_NOK_texture_from_pixmap, EGL_NV_3dvision_surface, EGL_NV_context_priority_realtime, EGL_NV_coverage_sample, EGL_NV_coverage_sample_resolve, EGL_NV_cuda_event, EGL_NV_depth_nonlinear, EGL_NV_device_cuda, EGL_NV_native_query, EGL_NV_post_convert_rounding, EGL_NV_post_sub_buffer, EGL_NV_robustness_video_memory_purge, EGL_NV_stream_consumer_gltexture_yuv, EGL_NV_stream_cross_display, EGL_NV_stream_cross_object, EGL_NV_stream_cross_partition, EGL_NV_stream_cross_process, EGL_NV_stream_cross_system, EGL_NV_stream_fifo_next, EGL_NV_stream_fifo_synchronous, EGL_NV_stream_flush, EGL_NV_stream_frame_limits, EGL_NV_stream_metadata, EGL_NV_stream_remote, EGL_NV_stream_reset, EGL_NV_stream_socket, EGL_NV_stream_socket_inet, EGL_NV_stream_socket_unix, EGL_NV_stream_sync, EGL_NV_sync, EGL_NV_system_time, EGL_TIZEN_image_native_buffer, EGL_TIZEN_image_native_surface Loader: True Local files: False Omit khrplatform: False Commandline: --api="egl=1.5" --generator="c" --spec="egl" --extensions="EGL_ANDROID_blob_cache,EGL_ANDROID_create_native_client_buffer,EGL_ANDROID_framebuffer_target,EGL_ANDROID_front_buffer_auto_refresh,EGL_ANDROID_get_frame_timestamps,EGL_ANDROID_get_native_client_buffer,EGL_ANDROID_image_native_buffer,EGL_ANDROID_native_fence_sync,EGL_ANDROID_presentation_time,EGL_ANDROID_recordable,EGL_ANGLE_d3d_share_handle_client_buffer,EGL_ANGLE_device_d3d,EGL_ANGLE_query_surface_pointer,EGL_ANGLE_surface_d3d_texture_2d_share_handle,EGL_ANGLE_window_fixed_size,EGL_ARM_implicit_external_sync,EGL_ARM_pixmap_multisample_discard,EGL_EXT_bind_to_front,EGL_EXT_buffer_age,EGL_EXT_client_extensions,EGL_EXT_client_sync,EGL_EXT_compositor,EGL_EXT_create_context_robustness,EGL_EXT_device_base,EGL_EXT_device_drm,EGL_EXT_device_enumeration,EGL_EXT_device_openwf,EGL_EXT_device_query,EGL_EXT_gl_colorspace_bt2020_linear,EGL_EXT_gl_colorspace_bt2020_pq,EGL_EXT_gl_colorspace_display_p3,EGL_EXT_gl_colorspace_display_p3_linear,EGL_EXT_gl_colorspace_display_p3_passthrough,EGL_EXT_gl_colorspace_scrgb,EGL_EXT_gl_colorspace_scrgb_linear,EGL_EXT_image_dma_buf_import,EGL_EXT_image_dma_buf_import_modifiers,EGL_EXT_image_gl_colorspace,EGL_EXT_image_implicit_sync_control,EGL_EXT_multiview_window,EGL_EXT_output_base,EGL_EXT_output_drm,EGL_EXT_output_openwf,EGL_EXT_pixel_format_float,EGL_EXT_platform_base,EGL_EXT_platform_device,EGL_EXT_platform_wayland,EGL_EXT_platform_x11,EGL_EXT_protected_content,EGL_EXT_protected_surface,EGL_EXT_stream_consumer_egloutput,EGL_EXT_surface_CTA861_3_metadata,EGL_EXT_surface_SMPTE2086_metadata,EGL_EXT_swap_buffers_with_damage,EGL_EXT_sync_reuse,EGL_EXT_yuv_surface,EGL_HI_clientpixmap,EGL_HI_colorformats,EGL_IMG_context_priority,EGL_IMG_image_plane_attribs,EGL_KHR_cl_event,EGL_KHR_cl_event2,EGL_KHR_client_get_all_proc_addresses,EGL_KHR_config_attribs,EGL_KHR_context_flush_control,EGL_KHR_create_context,EGL_KHR_create_context_no_error,EGL_KHR_debug,EGL_KHR_display_reference,EGL_KHR_fence_sync,EGL_KHR_get_all_proc_addresses,EGL_KHR_gl_colorspace,EGL_KHR_gl_renderbuffer_image,EGL_KHR_gl_texture_2D_image,EGL_KHR_gl_texture_3D_image,EGL_KHR_gl_texture_cubemap_image,EGL_KHR_image,EGL_KHR_image_base,EGL_KHR_image_pixmap,EGL_KHR_lock_surface,EGL_KHR_lock_surface2,EGL_KHR_lock_surface3,EGL_KHR_mutable_render_buffer,EGL_KHR_no_config_context,EGL_KHR_partial_update,EGL_KHR_platform_android,EGL_KHR_platform_gbm,EGL_KHR_platform_wayland,EGL_KHR_platform_x11,EGL_KHR_reusable_sync,EGL_KHR_stream,EGL_KHR_stream_attrib,EGL_KHR_stream_consumer_gltexture,EGL_KHR_stream_cross_process_fd,EGL_KHR_stream_fifo,EGL_KHR_stream_producer_aldatalocator,EGL_KHR_stream_producer_eglsurface,EGL_KHR_surfaceless_context,EGL_KHR_swap_buffers_with_damage,EGL_KHR_vg_parent_image,EGL_KHR_wait_sync,EGL_MESA_drm_image,EGL_MESA_image_dma_buf_export,EGL_MESA_platform_gbm,EGL_MESA_platform_surfaceless,EGL_NOK_swap_region,EGL_NOK_swap_region2,EGL_NOK_texture_from_pixmap,EGL_NV_3dvision_surface,EGL_NV_context_priority_realtime,EGL_NV_coverage_sample,EGL_NV_coverage_sample_resolve,EGL_NV_cuda_event,EGL_NV_depth_nonlinear,EGL_NV_device_cuda,EGL_NV_native_query,EGL_NV_post_convert_rounding,EGL_NV_post_sub_buffer,EGL_NV_robustness_video_memory_purge,EGL_NV_stream_consumer_gltexture_yuv,EGL_NV_stream_cross_display,EGL_NV_stream_cross_object,EGL_NV_stream_cross_partition,EGL_NV_stream_cross_process,EGL_NV_stream_cross_system,EGL_NV_stream_fifo_next,EGL_NV_stream_fifo_synchronous,EGL_NV_stream_flush,EGL_NV_stream_frame_limits,EGL_NV_stream_metadata,EGL_NV_stream_remote,EGL_NV_stream_reset,EGL_NV_stream_socket,EGL_NV_stream_socket_inet,EGL_NV_stream_socket_unix,EGL_NV_stream_sync,EGL_NV_sync,EGL_NV_system_time,EGL_TIZEN_image_native_buffer,EGL_TIZEN_image_native_surface" Online: Too many extensions */ #include #include #include #include int gladLoadEGL(void) { return gladLoadEGLLoader((GLADloadproc)eglGetProcAddress); } PFNEGLSETBLOBCACHEFUNCSANDROIDPROC glad_eglSetBlobCacheFuncsANDROID; PFNEGLCREATENATIVECLIENTBUFFERANDROIDPROC glad_eglCreateNativeClientBufferANDROID; PFNEGLGETCOMPOSITORTIMINGSUPPORTEDANDROIDPROC glad_eglGetCompositorTimingSupportedANDROID; PFNEGLGETCOMPOSITORTIMINGANDROIDPROC glad_eglGetCompositorTimingANDROID; PFNEGLGETNEXTFRAMEIDANDROIDPROC glad_eglGetNextFrameIdANDROID; PFNEGLGETFRAMETIMESTAMPSUPPORTEDANDROIDPROC glad_eglGetFrameTimestampSupportedANDROID; PFNEGLGETFRAMETIMESTAMPSANDROIDPROC glad_eglGetFrameTimestampsANDROID; PFNEGLGETNATIVECLIENTBUFFERANDROIDPROC glad_eglGetNativeClientBufferANDROID; PFNEGLDUPNATIVEFENCEFDANDROIDPROC glad_eglDupNativeFenceFDANDROID; PFNEGLPRESENTATIONTIMEANDROIDPROC glad_eglPresentationTimeANDROID; PFNEGLQUERYSURFACEPOINTERANGLEPROC glad_eglQuerySurfacePointerANGLE; PFNEGLCLIENTSIGNALSYNCEXTPROC glad_eglClientSignalSyncEXT; PFNEGLCOMPOSITORSETCONTEXTLISTEXTPROC glad_eglCompositorSetContextListEXT; PFNEGLCOMPOSITORSETCONTEXTATTRIBUTESEXTPROC glad_eglCompositorSetContextAttributesEXT; PFNEGLCOMPOSITORSETWINDOWLISTEXTPROC glad_eglCompositorSetWindowListEXT; PFNEGLCOMPOSITORSETWINDOWATTRIBUTESEXTPROC glad_eglCompositorSetWindowAttributesEXT; PFNEGLCOMPOSITORBINDTEXWINDOWEXTPROC glad_eglCompositorBindTexWindowEXT; PFNEGLCOMPOSITORSETSIZEEXTPROC glad_eglCompositorSetSizeEXT; PFNEGLCOMPOSITORSWAPPOLICYEXTPROC glad_eglCompositorSwapPolicyEXT; PFNEGLQUERYDEVICEATTRIBEXTPROC glad_eglQueryDeviceAttribEXT; PFNEGLQUERYDEVICESTRINGEXTPROC glad_eglQueryDeviceStringEXT; PFNEGLQUERYDEVICESEXTPROC glad_eglQueryDevicesEXT; PFNEGLQUERYDISPLAYATTRIBEXTPROC glad_eglQueryDisplayAttribEXT; PFNEGLQUERYDMABUFFORMATSEXTPROC glad_eglQueryDmaBufFormatsEXT; PFNEGLQUERYDMABUFMODIFIERSEXTPROC glad_eglQueryDmaBufModifiersEXT; PFNEGLGETOUTPUTLAYERSEXTPROC glad_eglGetOutputLayersEXT; PFNEGLGETOUTPUTPORTSEXTPROC glad_eglGetOutputPortsEXT; PFNEGLOUTPUTLAYERATTRIBEXTPROC glad_eglOutputLayerAttribEXT; PFNEGLQUERYOUTPUTLAYERATTRIBEXTPROC glad_eglQueryOutputLayerAttribEXT; PFNEGLQUERYOUTPUTLAYERSTRINGEXTPROC glad_eglQueryOutputLayerStringEXT; PFNEGLOUTPUTPORTATTRIBEXTPROC glad_eglOutputPortAttribEXT; PFNEGLQUERYOUTPUTPORTATTRIBEXTPROC glad_eglQueryOutputPortAttribEXT; PFNEGLQUERYOUTPUTPORTSTRINGEXTPROC glad_eglQueryOutputPortStringEXT; PFNEGLGETPLATFORMDISPLAYEXTPROC glad_eglGetPlatformDisplayEXT; PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC glad_eglCreatePlatformWindowSurfaceEXT; PFNEGLCREATEPLATFORMPIXMAPSURFACEEXTPROC glad_eglCreatePlatformPixmapSurfaceEXT; PFNEGLSTREAMCONSUMEROUTPUTEXTPROC glad_eglStreamConsumerOutputEXT; PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC glad_eglSwapBuffersWithDamageEXT; PFNEGLUNSIGNALSYNCEXTPROC glad_eglUnsignalSyncEXT; PFNEGLCREATEPIXMAPSURFACEHIPROC glad_eglCreatePixmapSurfaceHI; PFNEGLCREATESYNC64KHRPROC glad_eglCreateSync64KHR; PFNEGLDEBUGMESSAGECONTROLKHRPROC glad_eglDebugMessageControlKHR; PFNEGLQUERYDEBUGKHRPROC glad_eglQueryDebugKHR; PFNEGLLABELOBJECTKHRPROC glad_eglLabelObjectKHR; PFNEGLQUERYDISPLAYATTRIBKHRPROC glad_eglQueryDisplayAttribKHR; PFNEGLCREATESYNCKHRPROC glad_eglCreateSyncKHR; PFNEGLDESTROYSYNCKHRPROC glad_eglDestroySyncKHR; PFNEGLCLIENTWAITSYNCKHRPROC glad_eglClientWaitSyncKHR; PFNEGLGETSYNCATTRIBKHRPROC glad_eglGetSyncAttribKHR; PFNEGLCREATEIMAGEKHRPROC glad_eglCreateImageKHR; PFNEGLDESTROYIMAGEKHRPROC glad_eglDestroyImageKHR; PFNEGLLOCKSURFACEKHRPROC glad_eglLockSurfaceKHR; PFNEGLUNLOCKSURFACEKHRPROC glad_eglUnlockSurfaceKHR; PFNEGLQUERYSURFACE64KHRPROC glad_eglQuerySurface64KHR; PFNEGLSETDAMAGEREGIONKHRPROC glad_eglSetDamageRegionKHR; PFNEGLSIGNALSYNCKHRPROC glad_eglSignalSyncKHR; PFNEGLCREATESTREAMKHRPROC glad_eglCreateStreamKHR; PFNEGLDESTROYSTREAMKHRPROC glad_eglDestroyStreamKHR; PFNEGLSTREAMATTRIBKHRPROC glad_eglStreamAttribKHR; PFNEGLQUERYSTREAMKHRPROC glad_eglQueryStreamKHR; PFNEGLQUERYSTREAMU64KHRPROC glad_eglQueryStreamu64KHR; PFNEGLCREATESTREAMATTRIBKHRPROC glad_eglCreateStreamAttribKHR; PFNEGLSETSTREAMATTRIBKHRPROC glad_eglSetStreamAttribKHR; PFNEGLQUERYSTREAMATTRIBKHRPROC glad_eglQueryStreamAttribKHR; PFNEGLSTREAMCONSUMERACQUIREATTRIBKHRPROC glad_eglStreamConsumerAcquireAttribKHR; PFNEGLSTREAMCONSUMERRELEASEATTRIBKHRPROC glad_eglStreamConsumerReleaseAttribKHR; PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHRPROC glad_eglStreamConsumerGLTextureExternalKHR; PFNEGLSTREAMCONSUMERACQUIREKHRPROC glad_eglStreamConsumerAcquireKHR; PFNEGLSTREAMCONSUMERRELEASEKHRPROC glad_eglStreamConsumerReleaseKHR; PFNEGLGETSTREAMFILEDESCRIPTORKHRPROC glad_eglGetStreamFileDescriptorKHR; PFNEGLCREATESTREAMFROMFILEDESCRIPTORKHRPROC glad_eglCreateStreamFromFileDescriptorKHR; PFNEGLQUERYSTREAMTIMEKHRPROC glad_eglQueryStreamTimeKHR; PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC glad_eglCreateStreamProducerSurfaceKHR; PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC glad_eglSwapBuffersWithDamageKHR; PFNEGLWAITSYNCKHRPROC glad_eglWaitSyncKHR; PFNEGLCREATEDRMIMAGEMESAPROC glad_eglCreateDRMImageMESA; PFNEGLEXPORTDRMIMAGEMESAPROC glad_eglExportDRMImageMESA; PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC glad_eglExportDMABUFImageQueryMESA; PFNEGLEXPORTDMABUFIMAGEMESAPROC glad_eglExportDMABUFImageMESA; PFNEGLSWAPBUFFERSREGIONNOKPROC glad_eglSwapBuffersRegionNOK; PFNEGLSWAPBUFFERSREGION2NOKPROC glad_eglSwapBuffersRegion2NOK; PFNEGLQUERYNATIVEDISPLAYNVPROC glad_eglQueryNativeDisplayNV; PFNEGLQUERYNATIVEWINDOWNVPROC glad_eglQueryNativeWindowNV; PFNEGLQUERYNATIVEPIXMAPNVPROC glad_eglQueryNativePixmapNV; PFNEGLPOSTSUBBUFFERNVPROC glad_eglPostSubBufferNV; PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALATTRIBSNVPROC glad_eglStreamConsumerGLTextureExternalAttribsNV; PFNEGLSTREAMFLUSHNVPROC glad_eglStreamFlushNV; PFNEGLQUERYDISPLAYATTRIBNVPROC glad_eglQueryDisplayAttribNV; PFNEGLSETSTREAMMETADATANVPROC glad_eglSetStreamMetadataNV; PFNEGLQUERYSTREAMMETADATANVPROC glad_eglQueryStreamMetadataNV; PFNEGLRESETSTREAMNVPROC glad_eglResetStreamNV; PFNEGLCREATESTREAMSYNCNVPROC glad_eglCreateStreamSyncNV; PFNEGLCREATEFENCESYNCNVPROC glad_eglCreateFenceSyncNV; PFNEGLDESTROYSYNCNVPROC glad_eglDestroySyncNV; PFNEGLFENCENVPROC glad_eglFenceNV; PFNEGLCLIENTWAITSYNCNVPROC glad_eglClientWaitSyncNV; PFNEGLSIGNALSYNCNVPROC glad_eglSignalSyncNV; PFNEGLGETSYNCATTRIBNVPROC glad_eglGetSyncAttribNV; PFNEGLGETSYSTEMTIMEFREQUENCYNVPROC glad_eglGetSystemTimeFrequencyNV; PFNEGLGETSYSTEMTIMENVPROC glad_eglGetSystemTimeNV; static void load_EGL_ANDROID_blob_cache(GLADloadproc load) { glad_eglSetBlobCacheFuncsANDROID = (PFNEGLSETBLOBCACHEFUNCSANDROIDPROC)load("eglSetBlobCacheFuncsANDROID"); } static void load_EGL_ANDROID_create_native_client_buffer(GLADloadproc load) { glad_eglCreateNativeClientBufferANDROID = (PFNEGLCREATENATIVECLIENTBUFFERANDROIDPROC)load("eglCreateNativeClientBufferANDROID"); } static void load_EGL_ANDROID_get_frame_timestamps(GLADloadproc load) { glad_eglGetCompositorTimingSupportedANDROID = (PFNEGLGETCOMPOSITORTIMINGSUPPORTEDANDROIDPROC)load("eglGetCompositorTimingSupportedANDROID"); glad_eglGetCompositorTimingANDROID = (PFNEGLGETCOMPOSITORTIMINGANDROIDPROC)load("eglGetCompositorTimingANDROID"); glad_eglGetNextFrameIdANDROID = (PFNEGLGETNEXTFRAMEIDANDROIDPROC)load("eglGetNextFrameIdANDROID"); glad_eglGetFrameTimestampSupportedANDROID = (PFNEGLGETFRAMETIMESTAMPSUPPORTEDANDROIDPROC)load("eglGetFrameTimestampSupportedANDROID"); glad_eglGetFrameTimestampsANDROID = (PFNEGLGETFRAMETIMESTAMPSANDROIDPROC)load("eglGetFrameTimestampsANDROID"); } static void load_EGL_ANDROID_get_native_client_buffer(GLADloadproc load) { glad_eglGetNativeClientBufferANDROID = (PFNEGLGETNATIVECLIENTBUFFERANDROIDPROC)load("eglGetNativeClientBufferANDROID"); } static void load_EGL_ANDROID_native_fence_sync(GLADloadproc load) { glad_eglDupNativeFenceFDANDROID = (PFNEGLDUPNATIVEFENCEFDANDROIDPROC)load("eglDupNativeFenceFDANDROID"); } static void load_EGL_ANDROID_presentation_time(GLADloadproc load) { glad_eglPresentationTimeANDROID = (PFNEGLPRESENTATIONTIMEANDROIDPROC)load("eglPresentationTimeANDROID"); } static void load_EGL_ANGLE_query_surface_pointer(GLADloadproc load) { glad_eglQuerySurfacePointerANGLE = (PFNEGLQUERYSURFACEPOINTERANGLEPROC)load("eglQuerySurfacePointerANGLE"); } static void load_EGL_EXT_client_sync(GLADloadproc load) { glad_eglClientSignalSyncEXT = (PFNEGLCLIENTSIGNALSYNCEXTPROC)load("eglClientSignalSyncEXT"); } static void load_EGL_EXT_compositor(GLADloadproc load) { glad_eglCompositorSetContextListEXT = (PFNEGLCOMPOSITORSETCONTEXTLISTEXTPROC)load("eglCompositorSetContextListEXT"); glad_eglCompositorSetContextAttributesEXT = (PFNEGLCOMPOSITORSETCONTEXTATTRIBUTESEXTPROC)load("eglCompositorSetContextAttributesEXT"); glad_eglCompositorSetWindowListEXT = (PFNEGLCOMPOSITORSETWINDOWLISTEXTPROC)load("eglCompositorSetWindowListEXT"); glad_eglCompositorSetWindowAttributesEXT = (PFNEGLCOMPOSITORSETWINDOWATTRIBUTESEXTPROC)load("eglCompositorSetWindowAttributesEXT"); glad_eglCompositorBindTexWindowEXT = (PFNEGLCOMPOSITORBINDTEXWINDOWEXTPROC)load("eglCompositorBindTexWindowEXT"); glad_eglCompositorSetSizeEXT = (PFNEGLCOMPOSITORSETSIZEEXTPROC)load("eglCompositorSetSizeEXT"); glad_eglCompositorSwapPolicyEXT = (PFNEGLCOMPOSITORSWAPPOLICYEXTPROC)load("eglCompositorSwapPolicyEXT"); } static void load_EGL_EXT_device_base(GLADloadproc load) { glad_eglQueryDeviceAttribEXT = (PFNEGLQUERYDEVICEATTRIBEXTPROC)load("eglQueryDeviceAttribEXT"); glad_eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)load("eglQueryDeviceStringEXT"); glad_eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC)load("eglQueryDevicesEXT"); glad_eglQueryDisplayAttribEXT = (PFNEGLQUERYDISPLAYATTRIBEXTPROC)load("eglQueryDisplayAttribEXT"); } static void load_EGL_EXT_device_enumeration(GLADloadproc load) { glad_eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC)load("eglQueryDevicesEXT"); } static void load_EGL_EXT_device_query(GLADloadproc load) { glad_eglQueryDeviceAttribEXT = (PFNEGLQUERYDEVICEATTRIBEXTPROC)load("eglQueryDeviceAttribEXT"); glad_eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)load("eglQueryDeviceStringEXT"); glad_eglQueryDisplayAttribEXT = (PFNEGLQUERYDISPLAYATTRIBEXTPROC)load("eglQueryDisplayAttribEXT"); } static void load_EGL_EXT_image_dma_buf_import_modifiers(GLADloadproc load) { glad_eglQueryDmaBufFormatsEXT = (PFNEGLQUERYDMABUFFORMATSEXTPROC)load("eglQueryDmaBufFormatsEXT"); glad_eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)load("eglQueryDmaBufModifiersEXT"); } static void load_EGL_EXT_output_base(GLADloadproc load) { glad_eglGetOutputLayersEXT = (PFNEGLGETOUTPUTLAYERSEXTPROC)load("eglGetOutputLayersEXT"); glad_eglGetOutputPortsEXT = (PFNEGLGETOUTPUTPORTSEXTPROC)load("eglGetOutputPortsEXT"); glad_eglOutputLayerAttribEXT = (PFNEGLOUTPUTLAYERATTRIBEXTPROC)load("eglOutputLayerAttribEXT"); glad_eglQueryOutputLayerAttribEXT = (PFNEGLQUERYOUTPUTLAYERATTRIBEXTPROC)load("eglQueryOutputLayerAttribEXT"); glad_eglQueryOutputLayerStringEXT = (PFNEGLQUERYOUTPUTLAYERSTRINGEXTPROC)load("eglQueryOutputLayerStringEXT"); glad_eglOutputPortAttribEXT = (PFNEGLOUTPUTPORTATTRIBEXTPROC)load("eglOutputPortAttribEXT"); glad_eglQueryOutputPortAttribEXT = (PFNEGLQUERYOUTPUTPORTATTRIBEXTPROC)load("eglQueryOutputPortAttribEXT"); glad_eglQueryOutputPortStringEXT = (PFNEGLQUERYOUTPUTPORTSTRINGEXTPROC)load("eglQueryOutputPortStringEXT"); } static void load_EGL_EXT_platform_base(GLADloadproc load) { glad_eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)load("eglGetPlatformDisplayEXT"); glad_eglCreatePlatformWindowSurfaceEXT = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)load("eglCreatePlatformWindowSurfaceEXT"); glad_eglCreatePlatformPixmapSurfaceEXT = (PFNEGLCREATEPLATFORMPIXMAPSURFACEEXTPROC)load("eglCreatePlatformPixmapSurfaceEXT"); } static void load_EGL_EXT_stream_consumer_egloutput(GLADloadproc load) { glad_eglStreamConsumerOutputEXT = (PFNEGLSTREAMCONSUMEROUTPUTEXTPROC)load("eglStreamConsumerOutputEXT"); } static void load_EGL_EXT_swap_buffers_with_damage(GLADloadproc load) { glad_eglSwapBuffersWithDamageEXT = (PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC)load("eglSwapBuffersWithDamageEXT"); } static void load_EGL_EXT_sync_reuse(GLADloadproc load) { glad_eglUnsignalSyncEXT = (PFNEGLUNSIGNALSYNCEXTPROC)load("eglUnsignalSyncEXT"); } static void load_EGL_HI_clientpixmap(GLADloadproc load) { glad_eglCreatePixmapSurfaceHI = (PFNEGLCREATEPIXMAPSURFACEHIPROC)load("eglCreatePixmapSurfaceHI"); } static void load_EGL_KHR_cl_event2(GLADloadproc load) { glad_eglCreateSync64KHR = (PFNEGLCREATESYNC64KHRPROC)load("eglCreateSync64KHR"); } static void load_EGL_KHR_debug(GLADloadproc load) { glad_eglDebugMessageControlKHR = (PFNEGLDEBUGMESSAGECONTROLKHRPROC)load("eglDebugMessageControlKHR"); glad_eglQueryDebugKHR = (PFNEGLQUERYDEBUGKHRPROC)load("eglQueryDebugKHR"); glad_eglLabelObjectKHR = (PFNEGLLABELOBJECTKHRPROC)load("eglLabelObjectKHR"); } static void load_EGL_KHR_display_reference(GLADloadproc load) { glad_eglQueryDisplayAttribKHR = (PFNEGLQUERYDISPLAYATTRIBKHRPROC)load("eglQueryDisplayAttribKHR"); } static void load_EGL_KHR_fence_sync(GLADloadproc load) { glad_eglCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC)load("eglCreateSyncKHR"); glad_eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)load("eglDestroySyncKHR"); glad_eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)load("eglClientWaitSyncKHR"); glad_eglGetSyncAttribKHR = (PFNEGLGETSYNCATTRIBKHRPROC)load("eglGetSyncAttribKHR"); } static void load_EGL_KHR_image(GLADloadproc load) { glad_eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)load("eglCreateImageKHR"); glad_eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)load("eglDestroyImageKHR"); } static void load_EGL_KHR_image_base(GLADloadproc load) { glad_eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)load("eglCreateImageKHR"); glad_eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)load("eglDestroyImageKHR"); } static void load_EGL_KHR_lock_surface(GLADloadproc load) { glad_eglLockSurfaceKHR = (PFNEGLLOCKSURFACEKHRPROC)load("eglLockSurfaceKHR"); glad_eglUnlockSurfaceKHR = (PFNEGLUNLOCKSURFACEKHRPROC)load("eglUnlockSurfaceKHR"); } static void load_EGL_KHR_lock_surface3(GLADloadproc load) { glad_eglLockSurfaceKHR = (PFNEGLLOCKSURFACEKHRPROC)load("eglLockSurfaceKHR"); glad_eglUnlockSurfaceKHR = (PFNEGLUNLOCKSURFACEKHRPROC)load("eglUnlockSurfaceKHR"); glad_eglQuerySurface64KHR = (PFNEGLQUERYSURFACE64KHRPROC)load("eglQuerySurface64KHR"); } static void load_EGL_KHR_partial_update(GLADloadproc load) { glad_eglSetDamageRegionKHR = (PFNEGLSETDAMAGEREGIONKHRPROC)load("eglSetDamageRegionKHR"); } static void load_EGL_KHR_reusable_sync(GLADloadproc load) { glad_eglCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC)load("eglCreateSyncKHR"); glad_eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)load("eglDestroySyncKHR"); glad_eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)load("eglClientWaitSyncKHR"); glad_eglSignalSyncKHR = (PFNEGLSIGNALSYNCKHRPROC)load("eglSignalSyncKHR"); glad_eglGetSyncAttribKHR = (PFNEGLGETSYNCATTRIBKHRPROC)load("eglGetSyncAttribKHR"); } static void load_EGL_KHR_stream(GLADloadproc load) { glad_eglCreateStreamKHR = (PFNEGLCREATESTREAMKHRPROC)load("eglCreateStreamKHR"); glad_eglDestroyStreamKHR = (PFNEGLDESTROYSTREAMKHRPROC)load("eglDestroyStreamKHR"); glad_eglStreamAttribKHR = (PFNEGLSTREAMATTRIBKHRPROC)load("eglStreamAttribKHR"); glad_eglQueryStreamKHR = (PFNEGLQUERYSTREAMKHRPROC)load("eglQueryStreamKHR"); glad_eglQueryStreamu64KHR = (PFNEGLQUERYSTREAMU64KHRPROC)load("eglQueryStreamu64KHR"); } static void load_EGL_KHR_stream_attrib(GLADloadproc load) { glad_eglCreateStreamAttribKHR = (PFNEGLCREATESTREAMATTRIBKHRPROC)load("eglCreateStreamAttribKHR"); glad_eglSetStreamAttribKHR = (PFNEGLSETSTREAMATTRIBKHRPROC)load("eglSetStreamAttribKHR"); glad_eglQueryStreamAttribKHR = (PFNEGLQUERYSTREAMATTRIBKHRPROC)load("eglQueryStreamAttribKHR"); glad_eglStreamConsumerAcquireAttribKHR = (PFNEGLSTREAMCONSUMERACQUIREATTRIBKHRPROC)load("eglStreamConsumerAcquireAttribKHR"); glad_eglStreamConsumerReleaseAttribKHR = (PFNEGLSTREAMCONSUMERRELEASEATTRIBKHRPROC)load("eglStreamConsumerReleaseAttribKHR"); } static void load_EGL_KHR_stream_consumer_gltexture(GLADloadproc load) { glad_eglStreamConsumerGLTextureExternalKHR = (PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHRPROC)load("eglStreamConsumerGLTextureExternalKHR"); glad_eglStreamConsumerAcquireKHR = (PFNEGLSTREAMCONSUMERACQUIREKHRPROC)load("eglStreamConsumerAcquireKHR"); glad_eglStreamConsumerReleaseKHR = (PFNEGLSTREAMCONSUMERRELEASEKHRPROC)load("eglStreamConsumerReleaseKHR"); } static void load_EGL_KHR_stream_cross_process_fd(GLADloadproc load) { glad_eglGetStreamFileDescriptorKHR = (PFNEGLGETSTREAMFILEDESCRIPTORKHRPROC)load("eglGetStreamFileDescriptorKHR"); glad_eglCreateStreamFromFileDescriptorKHR = (PFNEGLCREATESTREAMFROMFILEDESCRIPTORKHRPROC)load("eglCreateStreamFromFileDescriptorKHR"); } static void load_EGL_KHR_stream_fifo(GLADloadproc load) { glad_eglQueryStreamTimeKHR = (PFNEGLQUERYSTREAMTIMEKHRPROC)load("eglQueryStreamTimeKHR"); } static void load_EGL_KHR_stream_producer_eglsurface(GLADloadproc load) { glad_eglCreateStreamProducerSurfaceKHR = (PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC)load("eglCreateStreamProducerSurfaceKHR"); } static void load_EGL_KHR_swap_buffers_with_damage(GLADloadproc load) { glad_eglSwapBuffersWithDamageKHR = (PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC)load("eglSwapBuffersWithDamageKHR"); } static void load_EGL_KHR_wait_sync(GLADloadproc load) { glad_eglWaitSyncKHR = (PFNEGLWAITSYNCKHRPROC)load("eglWaitSyncKHR"); } static void load_EGL_MESA_drm_image(GLADloadproc load) { glad_eglCreateDRMImageMESA = (PFNEGLCREATEDRMIMAGEMESAPROC)load("eglCreateDRMImageMESA"); glad_eglExportDRMImageMESA = (PFNEGLEXPORTDRMIMAGEMESAPROC)load("eglExportDRMImageMESA"); } static void load_EGL_MESA_image_dma_buf_export(GLADloadproc load) { glad_eglExportDMABUFImageQueryMESA = (PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC)load("eglExportDMABUFImageQueryMESA"); glad_eglExportDMABUFImageMESA = (PFNEGLEXPORTDMABUFIMAGEMESAPROC)load("eglExportDMABUFImageMESA"); } static void load_EGL_NOK_swap_region(GLADloadproc load) { glad_eglSwapBuffersRegionNOK = (PFNEGLSWAPBUFFERSREGIONNOKPROC)load("eglSwapBuffersRegionNOK"); } static void load_EGL_NOK_swap_region2(GLADloadproc load) { glad_eglSwapBuffersRegion2NOK = (PFNEGLSWAPBUFFERSREGION2NOKPROC)load("eglSwapBuffersRegion2NOK"); } static void load_EGL_NV_native_query(GLADloadproc load) { glad_eglQueryNativeDisplayNV = (PFNEGLQUERYNATIVEDISPLAYNVPROC)load("eglQueryNativeDisplayNV"); glad_eglQueryNativeWindowNV = (PFNEGLQUERYNATIVEWINDOWNVPROC)load("eglQueryNativeWindowNV"); glad_eglQueryNativePixmapNV = (PFNEGLQUERYNATIVEPIXMAPNVPROC)load("eglQueryNativePixmapNV"); } static void load_EGL_NV_post_sub_buffer(GLADloadproc load) { glad_eglPostSubBufferNV = (PFNEGLPOSTSUBBUFFERNVPROC)load("eglPostSubBufferNV"); } static void load_EGL_NV_stream_consumer_gltexture_yuv(GLADloadproc load) { glad_eglStreamConsumerGLTextureExternalAttribsNV = (PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALATTRIBSNVPROC)load("eglStreamConsumerGLTextureExternalAttribsNV"); } static void load_EGL_NV_stream_flush(GLADloadproc load) { glad_eglStreamFlushNV = (PFNEGLSTREAMFLUSHNVPROC)load("eglStreamFlushNV"); } static void load_EGL_NV_stream_metadata(GLADloadproc load) { glad_eglQueryDisplayAttribNV = (PFNEGLQUERYDISPLAYATTRIBNVPROC)load("eglQueryDisplayAttribNV"); glad_eglSetStreamMetadataNV = (PFNEGLSETSTREAMMETADATANVPROC)load("eglSetStreamMetadataNV"); glad_eglQueryStreamMetadataNV = (PFNEGLQUERYSTREAMMETADATANVPROC)load("eglQueryStreamMetadataNV"); } static void load_EGL_NV_stream_reset(GLADloadproc load) { glad_eglResetStreamNV = (PFNEGLRESETSTREAMNVPROC)load("eglResetStreamNV"); } static void load_EGL_NV_stream_sync(GLADloadproc load) { glad_eglCreateStreamSyncNV = (PFNEGLCREATESTREAMSYNCNVPROC)load("eglCreateStreamSyncNV"); } static void load_EGL_NV_sync(GLADloadproc load) { glad_eglCreateFenceSyncNV = (PFNEGLCREATEFENCESYNCNVPROC)load("eglCreateFenceSyncNV"); glad_eglDestroySyncNV = (PFNEGLDESTROYSYNCNVPROC)load("eglDestroySyncNV"); glad_eglFenceNV = (PFNEGLFENCENVPROC)load("eglFenceNV"); glad_eglClientWaitSyncNV = (PFNEGLCLIENTWAITSYNCNVPROC)load("eglClientWaitSyncNV"); glad_eglSignalSyncNV = (PFNEGLSIGNALSYNCNVPROC)load("eglSignalSyncNV"); glad_eglGetSyncAttribNV = (PFNEGLGETSYNCATTRIBNVPROC)load("eglGetSyncAttribNV"); } static void load_EGL_NV_system_time(GLADloadproc load) { glad_eglGetSystemTimeFrequencyNV = (PFNEGLGETSYSTEMTIMEFREQUENCYNVPROC)load("eglGetSystemTimeFrequencyNV"); glad_eglGetSystemTimeNV = (PFNEGLGETSYSTEMTIMENVPROC)load("eglGetSystemTimeNV"); } static int find_extensionsEGL(void) { return 1; } static void find_coreEGL(void) { } int gladLoadEGLLoader(GLADloadproc load) { (void) load; find_coreEGL(); if (!find_extensionsEGL()) return 0; load_EGL_ANDROID_blob_cache(load); load_EGL_ANDROID_create_native_client_buffer(load); load_EGL_ANDROID_get_frame_timestamps(load); load_EGL_ANDROID_get_native_client_buffer(load); load_EGL_ANDROID_native_fence_sync(load); load_EGL_ANDROID_presentation_time(load); load_EGL_ANGLE_query_surface_pointer(load); load_EGL_EXT_client_sync(load); load_EGL_EXT_compositor(load); load_EGL_EXT_device_base(load); load_EGL_EXT_device_enumeration(load); load_EGL_EXT_device_query(load); load_EGL_EXT_image_dma_buf_import_modifiers(load); load_EGL_EXT_output_base(load); load_EGL_EXT_platform_base(load); load_EGL_EXT_stream_consumer_egloutput(load); load_EGL_EXT_swap_buffers_with_damage(load); load_EGL_EXT_sync_reuse(load); load_EGL_HI_clientpixmap(load); load_EGL_KHR_cl_event2(load); load_EGL_KHR_debug(load); load_EGL_KHR_display_reference(load); load_EGL_KHR_fence_sync(load); load_EGL_KHR_image(load); load_EGL_KHR_image_base(load); load_EGL_KHR_lock_surface(load); load_EGL_KHR_lock_surface3(load); load_EGL_KHR_partial_update(load); load_EGL_KHR_reusable_sync(load); load_EGL_KHR_stream(load); load_EGL_KHR_stream_attrib(load); load_EGL_KHR_stream_consumer_gltexture(load); load_EGL_KHR_stream_cross_process_fd(load); load_EGL_KHR_stream_fifo(load); load_EGL_KHR_stream_producer_eglsurface(load); load_EGL_KHR_swap_buffers_with_damage(load); load_EGL_KHR_wait_sync(load); load_EGL_MESA_drm_image(load); load_EGL_MESA_image_dma_buf_export(load); load_EGL_NOK_swap_region(load); load_EGL_NOK_swap_region2(load); load_EGL_NV_native_query(load); load_EGL_NV_post_sub_buffer(load); load_EGL_NV_stream_consumer_gltexture_yuv(load); load_EGL_NV_stream_flush(load); load_EGL_NV_stream_metadata(load); load_EGL_NV_stream_reset(load); load_EGL_NV_stream_sync(load); load_EGL_NV_sync(load); load_EGL_NV_system_time(load); return 1; } ================================================ FILE: src/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.13) project(megaverse) add_definitions(-DGLM_ENABLE_EXPERIMENTAL) option(BUILD_GUI_APPS "Whether to build apps that require GUI (things like SDL2)" ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules/" ${CMAKE_MODULE_PATH}) include(cmake/util.cmake) common_settings() add_subdirectory(3rdparty) add_compile_options(-Wall -Wextra -Wdelete-non-virtual-dtor -pedantic -Werror) add_subdirectory(libs) add_subdirectory(apps) add_subdirectory(examples) add_subdirectory(test) ================================================ FILE: src/apps/CMakeLists.txt ================================================ find_package(Corrade REQUIRED Main) set(MAGNUM_DEPENDENCIES Corrade::Main Magnum::GL Magnum::Magnum Magnum::MeshTools Magnum::Primitives Magnum::SceneGraph Magnum::Shaders ) if (BUILD_GUI_APPS) set(VIEWER_APP_SOURCES viewer_app.cpp viewer_args.cpp) add_app_default(viewer_app "${VIEWER_APP_SOURCES}") # list of sources gotta be in quites target_link_libraries(viewer_app PRIVATE scenarios viewer Magnum::Application) endif() find_package(ZLIB) set(MEGAVERSE_TEST_APP_SOURCES megaverse_test_app.cpp viewer_args.cpp) add_app_default(megaverse_test_app "${MEGAVERSE_TEST_APP_SOURCES}") target_link_libraries(megaverse_test_app PRIVATE scenarios magnum_rendering ${MAGNUM_DEPENDENCIES} ${OpenCV_LIBS}) if (ZLIB_FOUND) target_link_libraries(megaverse_test_app PRIVATE ${ZLIB_LIBRARIES}) endif () if (NOT CORRADE_TARGET_APPLE) target_link_libraries(megaverse_test_app PRIVATE v4r_rendering) endif () # Make the executable a default target to build & run in Visual Studio set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT viewer) add_app_default(mazegen mazegen.cpp) target_link_libraries(mazegen PRIVATE mazes) ================================================ FILE: src/apps/mazegen.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void usage(std::ostream &out) { out << "Usage: mazegen [--help] [-m ] [-a ]" << std::endl; out << " [-s | -w -h ]" << std::endl; out << " [-t ] [-o ]" << std::endl; out << " [-f ]" << std::endl; out << std::endl; out << "Optional arguments" << std::endl; out << " --help " << "Show this message and exit" << std::endl; out << " -m " << "Maze type" << std::endl; out << " " << "0: Rectangular (default)" << std::endl; out << " " << "1: Hexagonal (triangular lattice)" << std::endl; out << " " << "2: Honeycomb" << std::endl; out << " " << "3: Circular" << std::endl; out << " " << "4: Circular (triangular lattice)" << std::endl; out << " " << "5: User defined graph" << std::endl; out << " -a " << "Algorithm type" << std::endl; out << " " << "0: Kruskal's algorithm (default)" << std::endl; out << " " << "1: Depth-first search" << std::endl; out << " " << "2: Breadth-first search" << std::endl; out << " " << "3: Loop-erased random walk" << std::endl; out << " " << "4: Prim's algorithm" << std::endl; out << " -s " << "Size (non-rectangular mazes, default: 20)" << std::endl; out << " -w,-h " << "Width and height (rectangular maze, default: 20)" << std::endl; out << " -t " << "Output type" << std::endl; out << " " << "0: svg output (default)" << std::endl; out << " " << "1: png output using gnuplot (.plt) intermediate " << std::endl; out << " -o " << "Prefix for .svg, .plt and .png outputs (default: maze)" << std::endl; } int main(int argc, char *argv[]) { std::string outputprefix = "maze", infile = ""; std::map optionmap{{"-m", 0}, {"-a", 0}, {"-s", 20}, {"-w", 20}, {"-h", 20}, {"-o", 0}, {"-f", 0}, {"--help", 0}, {"-t", 0}}; for (int i = 1; i < argc; i++) { if (optionmap.find(argv[i]) == optionmap.end()) { std::cerr << "Unknown argument " << argv[i] << "\n"; usage(std::cerr); return 1; } if (strcmp("-o", argv[i]) == 0) { if (i + 1 == argc) { std::cerr << "Missing output prefix" << std::endl; usage(std::cerr); return 1; } outputprefix = argv[++i]; continue; } else if (strcmp("-f", argv[i]) == 0) { if (i + 1 == argc) { std::cerr << "Missing maze input file" << std::endl; usage(std::cerr); return 1; } infile = argv[++i]; continue; } else if (strcmp("--help", argv[i]) == 0) { usage(std::cout); return 0; } if (i + 1 == argc) { std::cerr << "Missing option for argument " << argv[i] << std::endl; usage(std::cerr); return 1; } int x; try { x = std::stoi(argv[i + 1]); } catch (...) { std::cerr << "Invalid argument " << argv[i + 1] << " for option " << argv[i] << "\n"; usage(std::cerr); return 1; } optionmap[argv[i++]] = x; } Maze *maze; SpanningtreeAlgorithm *algorithm; switch (optionmap["-m"]) { case 0: if (optionmap["-w"] < 1 or optionmap["-h"] < 1) { std::cerr << "Invalide size " << optionmap["-w"] << "x" << optionmap["-h"] << " for rectangular maze\n"; usage(std::cerr); return 1; } std::cout << "Rectangular maze of size " << optionmap["-w"] << "x" << optionmap["-h"] << "\n"; maze = new RectangularMaze(optionmap["-w"], optionmap["-h"]); break; case 1: if (optionmap["-s"] < 1) { std::cerr << "Invalide size " << optionmap["-s"] << " for hexagonal maze with triangular lattice\n"; usage(std::cerr); return 1; } std::cout << "Hexagonal maze with triangular lattice of size " << optionmap["-s"] << "\n"; maze = new HexagonalMaze(optionmap["-s"]); break; case 2: if (optionmap["-s"] < 1) { std::cerr << "Invalide size " << optionmap["-s"] << " for honeycomb maze\n"; usage(std::cerr); return 1; } std::cout << "Honeycomb maze of size " << optionmap["-s"] << "\n"; maze = new HoneyCombMaze(optionmap["-s"]); break; case 3: if (optionmap["-s"] < 1) { std::cerr << "Invalide size " << optionmap["-s"] << " for circular maze\n"; usage(std::cerr); return 1; } std::cout << "Circular maze of size " << optionmap["-s"] << "\n"; maze = new CircularMaze(optionmap["-s"]); break; case 4: if (optionmap["-s"] < 1) { std::cerr << "Invalide size " << optionmap["-s"] << " for circular maze with triangular lattice\n"; usage(std::cerr); return 1; } std::cout << "Circular maze with triangular lattice of size " << optionmap["-s"] << "\n"; maze = new CircularHexagonMaze(optionmap["-s"]); break; case 5: if (infile == "") { std::cerr << "Graph description file not provided for user-defined graph\n"; usage(std::cerr); return 1; } std::cout << "User-defined graph\n"; maze = new UserMaze(infile); break; default: std::cerr << "Unknown maze type " << optionmap["-m"]; usage(std::cerr); return 1; } switch (optionmap["-a"]) { case 0: std::cout << "Maze generation using Kruskal's algorithm\n"; algorithm = new Kruskal; break; case 1: std::cout << "Maze generation using Depth-first search\n"; algorithm = new DepthFirstSearch; break; case 2: std::cout << "Maze generation using Breadth-first search\n"; algorithm = new BreadthFirstSearch; break; case 3: std::cout << "Maze generation using Loop-erased random walk\n"; algorithm = new LoopErasedRandomWalk; break; case 4: std::cout << "Maze generation using Prim's algorithm\n"; algorithm = new Prim; break; default: std::cerr << "Unknown algorithm type " << optionmap["-a"]; usage(std::cerr); return 1; } if (optionmap["-t"] < 0 or optionmap["-t"] > 1) { std::cerr << "Unknown output type " << optionmap["-a"]; usage(std::cerr); return 1; } int status = 0; std::cout << "Initialising graph..." << std::endl; maze->InitialiseGraph(); std::cout << "Generating maze..." << std::endl; maze->GenerateMaze(algorithm); if (optionmap["-t"] == 0) { std::cout << "Rendering maze to '" << outputprefix << ".svg'..." << std::endl; maze->PrintMazeSVG(outputprefix); } else { std::cout << "Exporting maze plotting parameters to '" << outputprefix << ".plt' ..." << std::endl; maze->PrintMazeGnuplot(outputprefix); std::cout << "Rendering maze to '" << outputprefix << ".png' using gnuplot..." << std::endl; status = system(("gnuplot '" + outputprefix + ".plt'").c_str()); } return status; } ================================================ FILE: src/apps/megaverse_test_app.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(CORRADE_TARGET_APPLE) #include #endif #include #include "viewer_args.hpp" using namespace Megaverse; // don't ask me, this is what waitKeyEx returns constexpr auto keyUp = 65362, keyLeft = 65361, keyRight = 65363, keyDown = 65364; std::string windowName(int envIdx, int agentIdx) { auto wname = std::to_string(envIdx) + std::to_string(agentIdx); return wname; } int mainLoop(VectorEnv &venv, EnvRenderer &renderer, bool viz, bool performanceTest, bool randomActions, int W, int H, bool useVulkan, int delayMs, int maxNumFrames) { if (performanceTest) randomActions = true; auto activeAgent = 0; int numFrames = 0, prevNumFrames = 0; const auto numEnvs = int(venv.envs.size()), numAgents = venv.envs.front()->getNumAgents(); if (viz) { for (int envIdx = 0; envIdx < numEnvs; ++envIdx) { for (int i = 0; i < numAgents; ++i) { const auto wname = windowName(envIdx, i); cv::namedWindow(wname); cv::moveWindow(wname, int(W * i * 1.1), int(H * envIdx * 1.1)); } } } venv.reset(); #if defined(ARTIFICIAL_MEMLEAK) std::vector> vvi; #endif auto rng = venv.envs.front()->getRng(); tprof().startTimer("fps_period"); bool shouldExit = false; do { tprof().startTimer("step"); venv.step(); tprof().pauseTimer("step"); for (int envIdx = 0; envIdx < int(venv.envs.size()); ++envIdx) { for (int i = 0; i < venv.envs[envIdx]->getNumAgents(); ++i) { const uint8_t *obsData = renderer.getObservation(envIdx, i); if (viz) { cv::Mat mat(H, W, CV_8UC4, (char *) obsData); cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR); if (!useVulkan) cv::flip(mat, mat, 0); cv::imshow(windowName(envIdx, i), mat); } } } if (viz) { auto latestAction = Action::Idle; auto key = cv::waitKeyEx(delayMs); switch (key) { case 'w': latestAction |= Action::Forward; break; case 's': latestAction |= Action::Backward; break; case 'a': latestAction |= Action::Left; break; case 'd': latestAction |= Action::Right; break; case ' ': latestAction |= Action::Jump; break; case keyLeft: latestAction |= Action::LookLeft; break; case keyRight: latestAction |= Action::LookRight; break; case keyUp: latestAction |= Action::LookUp; break; case keyDown: latestAction |= Action::LookDown; break; case '1': case '2': case '3': case '4': activeAgent = key - 1; break; default: break; } venv.envs.front()->setAction(activeAgent, latestAction); } if (randomActions) { for (auto & env : venv.envs) { for (int i = 0; i < env->getNumAgents(); ++i) { auto randomAction = randRange(0, int(Action::NumActions), rng); env->setAction(i, Action(1 << randomAction)); } } } numFrames += numAgents * numEnvs; if (numFrames > maxNumFrames) { shouldExit = true; TLOG(INFO) << "Done: " << numFrames; break; } else if (numFrames % 5000 == 0) { auto elapsedTimeSec = tprof().stopTimer("fps_period") / 1e6; tprof().startTimer("fps_period"); auto approxFps = float(numFrames - prevNumFrames) / elapsedTimeSec; prevNumFrames = numFrames; double vmUsage, residentSet; unixProcessMemUsage(vmUsage, residentSet); #if defined(ARTIFICIAL_MEMLEAK) // test artificial memleak vvi.emplace_back(1024 * 1024, 3); TLOG(INFO) << std::accumulate(vvi.back().begin(), vvi.back().end(), 0); #endif TLOG(INFO) << "Progress " << numFrames << "/" << maxNumFrames << ". Approx FPS: " << approxFps << ". VM usage: " << (long long)vmUsage << ". RSS: " << (long long)residentSet; } } while (!shouldExit); return numFrames; } int main(int argc, char** argv) { scenariosGlobalInit(); auto parser = viewerStandardArgParse("megaverse_test_app"); parser.add_description("This app is designed to test the parallel execution engine and batched renderer\n" "by simulating multiple environments at once. This app uses pretty much the same interface\n" "as the Python Gym environment, sans the Python bindings. Whenever there is a problem\n" "with the environment, it is much easier to debug this app directly, rather\n" "than debugging the same code through Python.\n\n" "Example, render 12 agents at the same time:\n" "megaverse_test_app --scenario Collect --visualize --num_envs 4 --num_simulation_threads 1 --num_agents 3 --hires\n\n" "Some performance figures for future reference (on 10-core Intel i9):\n" "megaverse_test_app --scenario Empty --performance_test --num_envs 64 --num_simulation_threads 1 --num_agents 1\n" "yields approximately 75000 FPS\n" "megaverse_test_app --scenario Collect --performance_test --num_envs 64 --num_simulation_threads 1 --num_agents 1\n" "yields approximately 27000 FPS\n"); parser.add_argument("--num_envs") .help("number of parallel environments to simulate") .default_value(64) .scan<'i', int>(); parser.add_argument("--num_simulation_threads") .help("number of parallel CPU threads to use for Bullet") .default_value(1) .scan<'i', int>(); parser.add_argument("--visualize") .help("Whether to render multiple environments on screen") .default_value(false) .implicit_value(true); parser.add_argument("--visualize") .help("Whether to render multiple environments on screen") .default_value(false) .implicit_value(true); parser.add_argument("--delay_ms") .help("Delay between rendered frames in milliseconds. Use only with --visualize") .default_value(1) .scan<'i', int>(); parser.add_argument("--performance_test") .help("Run for a limited number of env frames (currently 200000) to test performance. Uses random actions.") .default_value(false) .implicit_value(true); parser.add_argument("--hires") .help("Render at high resolution. Only use this parameter with --visualize and if the total number of agents is small") .default_value(false) .implicit_value(true); parser.add_argument("--user_actions") .help("Allows the user to control agents (otherwise will use randomly generated actions). Use only with --visualize") .default_value(false) .implicit_value(true); parseArgs(parser, argc, argv); const auto scenarioName = parser.get("--scenario"); const auto numAgents = parser.get("--num_agents"); const bool useVulkanRenderer = !parser.get("--use_opengl"); const int numEnvs = parser.get("--num_envs"); // to test vectorized env interface const int numSimulationThreads = parser.get("--num_simulation_threads"); const auto viz = parser.get("--visualize"); const auto delayMs = parser.get("--delay_ms"); const auto performanceTest = parser.get("--performance_test"); const auto hires = parser.get("--hires"); const bool randomActions = !parser.get("--user_actions"); const int W = hires ? 800 : 128, H = hires ? 450 : 72; TLOG(INFO) << "Rendering resolution is [" << W << "x" << H << "] per agent"; const int maxNumFrames = performanceTest ? 400'000 : 2'000'000'000; // FloatParams params{{Str::episodeLengthSec, 0.1f}}; FloatParams params{{}}; std::vector> envs; for (int i = 0; i < numEnvs; ++i) { envs.emplace_back(std::make_unique(scenarioName, numAgents, params)); envs[i]->seed(42 + i); } std::unique_ptr renderer; if (useVulkanRenderer) #if defined (CORRADE_TARGET_APPLE) TLOG(ERROR) << "Vulkan not supported on MacOS"; #else renderer = std::make_unique(envs, W, H, nullptr, false); #endif else { constexpr auto debugDraw = false; renderer = std::make_unique(envs, W, H, debugDraw); } VectorEnv vectorEnv{envs, *renderer, numSimulationThreads}; vectorEnv.reset(); tprof().startTimer("loop"); auto nFrames = mainLoop(vectorEnv, *renderer, viz, performanceTest, randomActions, W, H, useVulkanRenderer, delayMs, maxNumFrames); const auto usecPassed = tprof().stopTimer("loop"); tprof().stopTimer("step"); vectorEnv.close(); const auto fps = nFrames / (usecPassed / 1e6); TLOG(DEBUG) << "\n\n" << fps << " FPS! " << nFrames << " frames"; return EXIT_SUCCESS; } ================================================ FILE: src/apps/viewer_app.cpp ================================================ #include #include #include #include #include #include "viewer_args.hpp" using namespace Magnum; using namespace Magnum::Math::Literals; using namespace Megaverse; class ViewerApp: public Viewer { public: explicit ViewerApp(Envs &envs, unsigned int minimalLoopPeriod, bool useVulkanRenderer, const Arguments& arguments); private: void drawEvent() override; void tickEvent() override; void keyPressEvent(KeyEvent &event) override; void keyReleaseEvent(KeyEvent &event) override; void handleActions(const KeyEvent::Key &key, bool addAction); private: Action currAction = Action::Idle; Timeline timeline; }; ViewerApp::ViewerApp(Envs &envs, unsigned int minimalLoopPeriod, bool useVulkanRenderer, const Arguments& arguments) : Viewer{envs, useVulkanRenderer, nullptr, arguments} { setMinimalLoopPeriod(minimalLoopPeriod); timeline.start(); } void ViewerApp::tickEvent() { auto &env = envs[activeEnv]; env->setFrameDuration(timeline.previousFrameDuration()); env->setAction(activeAgent, currAction); currAction &= ~Action::Interact; env->step(); const auto done = env->isDone(); if (done) { TLOG(INFO) << "Done!"; std::ostringstream s; for (int i = 0; i < env->getNumAgents(); ++i) s << " " << env->getTotalReward(i); TLOG(INFO) << "Total reward: " << s.str(); s.clear(); for (int i = 0; i < env->getNumAgents(); ++i) s << " " << env->trueObjective(i); TLOG(INFO) << "True objectives: " << s.str(); env->reset(); } Viewer::step(std::vector(1, done)); } void ViewerApp::drawEvent() { Viewer::drawEvent(); timeline.nextFrame(); } void ViewerApp::keyPressEvent(KeyEvent &event) { Viewer::keyPressEvent(event); handleActions(event.key(), true); event.setAccepted(); } void ViewerApp::keyReleaseEvent(KeyEvent &event) { Viewer::keyReleaseEvent(event); handleActions(event.key(), false); event.setAccepted(); } void ViewerApp::handleActions(const KeyEvent::Key &key, bool addAction) { auto a = Action::Idle; switch(key) { case KeyEvent::Key::W: a = Action::Forward; break; case KeyEvent::Key::S: a = Action::Backward; break; case KeyEvent::Key::A: a = Action::Left; break; case KeyEvent::Key::D: a = Action::Right; break; case KeyEvent::Key::Left: a = Action::LookLeft; break; case KeyEvent::Key::Right: a = Action::LookRight; break; case KeyEvent::Key::Up: a = Action::LookUp; break; case KeyEvent::Key::Down: a = Action::LookDown; break; case KeyEvent::Key::Space: a = Action::Jump; break; case KeyEvent::Key::E: a = Action::Interact; break; default: break; } if (a == Action::Idle) return; if (addAction) currAction |= a; else currAction &= ~a; } int main(int argc, char** argv) { scenariosGlobalInit(); auto parser = viewerStandardArgParse("viewer_app"); parser.add_description("viewer_app can run any scenario in an interactive mode"); parser.add_argument("--desired_fps") .help("rendering framerate for human perception; RL agents percieve the world at 15 FPS to avoid frameskip, hence the default value.") .default_value(15) .scan<'i', int>(); parseArgs(parser, argc, argv); const auto scenarioName = parser.get("--scenario"); const auto numAgents = parser.get("--num_agents"); const auto desiredFps = parser.get("--desired_fps"); const bool useVulkanRenderer = !parser.get("--use_opengl"); FloatParams params{{Str::useUIRewardIndicators, 1.0f}}; auto env = std::make_unique(scenarioName, numAgents, params); #ifdef FIXED_SEED env->seed(42); #endif env->reset(); const unsigned int delayMs = 1000 / desiredFps; env->setSimulationResolution(1.0f / float(desiredFps)); Envs envs; envs.emplace_back(std::move(env)); ViewerApp app(envs, delayMs, useVulkanRenderer, {argc, argv}); return app.exec(); } ================================================ FILE: src/apps/viewer_args.cpp ================================================ #include #include #include "viewer_args.hpp" argparse::ArgumentParser Megaverse::viewerStandardArgParse(const std::string &appName) { argparse::ArgumentParser parser(appName); parser.add_argument("-l", "--list_scenarios") .help("list registered scenario names") .action([&](const auto &) { auto s = Scenario::registeredScenarios(); TLOG(INFO) << "Scenarios: " << s; }) .default_value(false) .implicit_value(true); parser.add_argument("--scenario") .help("name of the scenario to run") .default_value(std::string{"ObstaclesEasy"}); parser.add_argument("--num_agents") .help("size of the team") .default_value(2) .scan<'i', int>(); constexpr bool isApple = #if defined(CORRADE_TARGET_APPLE) true; #else false; #endif parser.add_argument("--use_opengl") .help("Whether to use OpenGL renderer instead of fast Vulkan renderer (currently Vulkan is only supported in Linux)") .default_value(isApple) .implicit_value(true); return parser; } void Megaverse::parseArgs(argparse::ArgumentParser &p, int argc, char** argv) { try { p.parse_args(argc, argv); } catch (const std::runtime_error &err) { std::cerr << err.what() << std::endl; std::cerr << p; std::exit(EXIT_FAILURE); } } ================================================ FILE: src/apps/viewer_args.hpp ================================================ #include namespace Megaverse { argparse::ArgumentParser viewerStandardArgParse(const std::string &appName); void parseArgs(argparse::ArgumentParser &p, int argc, char** argv); } ================================================ FILE: src/cmake/modules/FindCorrade.cmake ================================================ #.rst: # Find Corrade # ------------ # # Finds the Corrade library. Basic usage:: # # find_package(Corrade REQUIRED) # # This module tries to find the base Corrade library and then defines the # following: # # Corrade_FOUND - Whether the base library was found # CORRADE_LIB_SUFFIX_MODULE - Path to CorradeLibSuffix.cmake module # CORRADE_INCLUDE_INSTALL_PREFIX - Prefix where to put platform-independent # include and other files, defaults to ``.``. If a relative path is used, # it's relative to :variable:`CMAKE_INSTALL_PREFIX`. # # This command will try to find only the base library, not the optional # components, which are: # # Containers - Containers library # PluginManager - PluginManager library # TestSuite - TestSuite library # Utility - Utility library # rc - corrade-rc executable # # Example usage with specifying additional components is:: # # find_package(Corrade REQUIRED Utility TestSuite) # # For each component is then defined: # # Corrade_*_FOUND - Whether the component was found # Corrade::* - Component imported target # # The package is found if either debug or release version of each library is # found. If both debug and release libraries are found, proper version is # chosen based on actual build configuration of the project (i.e. Debug build # is linked to debug libraries, Release build to release libraries). # # Corrade conditionally defines ``CORRADE_IS_DEBUG_BUILD`` preprocessor # variable in case build configuration is ``Debug`` (not Corrade itself, but # build configuration of the project using it). Useful e.g. for selecting # proper plugin directory. # # Corrade defines the following custom target properties: # # CORRADE_CXX_STANDARD - C++ standard to require when compiling given # target. Does nothing if :variable:`CMAKE_CXX_FLAGS` already contains # particular standard setting flag or if given target contains # :prop_tgt:`CMAKE_CXX_STANDARD` property. Allowed value is 11, 14 or 17. # INTERFACE_CORRADE_CXX_STANDARD - C++ standard to require when using given # target. Does nothing if :variable:`CMAKE_CXX_FLAGS` already contains # particular standard setting flag or if given target contains # :prop_tgt:`CMAKE_CXX_STANDARD` property. Allowed value is 11, 14 or 17. # CORRADE_USE_PEDANTIC_FLAGS - Enable additional compiler/linker flags. # Boolean. # # These properties are inherited from directory properties, meaning that if you # set them on directories, they get implicitly set on all targets in given # directory (with a possibility to do target-specific overrides). All Corrade # libraries have the :prop_tgt:`INTERFACE_CORRADE_CXX_STANDARD` property set to # 11, meaning that you will always have at least C++11 enabled once you link to # any Corrade library. # # Features of found Corrade library are exposed in these variables: # # CORRADE_MSVC2019_COMPATIBILITY - Defined if compiled with compatibility # mode for MSVC 2019 # CORRADE_MSVC2017_COMPATIBILITY - Defined if compiled with compatibility # mode for MSVC 2017 # CORRADE_MSVC2015_COMPATIBILITY - Defined if compiled with compatibility # mode for MSVC 2015 # CORRADE_BUILD_DEPRECATED - Defined if compiled with deprecated APIs # included # CORRADE_BUILD_STATIC - Defined if compiled as static libraries. # Default are shared libraries. # CORRADE_BUILD_MULTITHREADED - Defined if compiled in a way that makes it # possible to safely use certain Corrade features simultaenously in multiple # threads # CORRADE_TARGET_UNIX - Defined if compiled for some Unix flavor # (Linux, BSD, macOS) # CORRADE_TARGET_APPLE - Defined if compiled for Apple platforms # CORRADE_TARGET_IOS - Defined if compiled for iOS (device or # simulator) # CORRADE_TARGET_IOS_SIMULATOR - Defined if compiled for iOS Simulator # CORRADE_TARGET_WINDOWS - Defined if compiled for Windows # CORRADE_TARGET_WINDOWS_RT - Defined if compiled for Windows RT # CORRADE_TARGET_EMSCRIPTEN - Defined if compiled for Emscripten # CORRADE_TARGET_ANDROID - Defined if compiled for Android # CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT - Defined if PluginManager # doesn't support dynamic plugin loading due to platform limitations # CORRADE_TESTSUITE_TARGET_XCTEST - Defined if TestSuite is targetting Xcode # XCTest # CORRADE_UTILITY_USE_ANSI_COLORS - Defined if ANSI escape sequences are used # for colored output with Utility::Debug on Windows # # Additionally these variables are defined for internal usage: # # CORRADE_INCLUDE_DIR - Root include dir # CORRADE_*_LIBRARY_DEBUG - Debug version of given library, if found # CORRADE_*_LIBRARY_RELEASE - Release version of given library, if found # CORRADE_*_EXECUTABLE - Location of given executable, if found # CORRADE_USE_MODULE - Path to UseCorrade.cmake module (included # automatically) # CORRADE_TESTSUITE_XCTEST_RUNNER - Path to XCTestRunner.mm.in file # CORRADE_TESTSUITE_ADB_RUNNER - Path to AdbRunner.sh file # CORRADE_PEDANTIC_COMPILER_OPTIONS - List of pedantic compiler options used # for targets with :prop_tgt:`CORRADE_USE_PEDANTIC_FLAGS` enabled # CORRADE_PEDANTIC_COMPILER_DEFINITIONS - List of pedantic compiler # definitions used for targets with :prop_tgt:`CORRADE_USE_PEDANTIC_FLAGS` # enabled # # Workflows without :prop_tgt:`IMPORTED` targets are deprecated and the # following variables are included just for backwards compatibility and only if # :variable:`CORRADE_BUILD_DEPRECATED` is enabled: # # CORRADE_CXX_FLAGS - Pedantic compile flags. Use # :prop_tgt:`CORRADE_USE_PEDANTIC_FLAGS` property or # :variable:`CORRADE_PEDANTIC_COMPILER_DEFINITIONS` / # :variable:`CORRADE_PEDANTIC_COMPILER_OPTIONS` list variables instead. # # Corrade provides these macros and functions: # # .. command:: corrade_add_test # # Add unit test using Corrade's TestSuite:: # # corrade_add_test( # ... # [LIBRARIES ...] # [FILES ...] # [ARGUMENTS ...]) # # Test name is also executable name. You can use ``LIBRARIES`` to specify # libraries to link with instead of using :command:`target_link_libraries()`. # The ``Corrade::TestSuite`` target is linked automatically to each test. Note # that the :command:`enable_testing()` function must be called explicitly. # Arguments passed after ``ARGUMENTS`` will be appended to the test # command line. ``ARGUMENTS`` are supported everywhere except when # ``CORRADE_TESTSUITE_TARGET_XCTEST`` is enabled. # # You can list files needed by the test in the ``FILES`` section. If given # filename is relative, it is treated relatively to `CMAKE_CURRENT_SOURCE_DIR`. # The files are added to the :prop_test:`REQUIRED_FILES` target property. On # Emscripten they are bundled to the executable and available in the virtual # filesystem root. On Android they are copied along the executable to the # target. In case of Emscripten and Android, if the file is absolute or # contains ``..``, only the leaf name is used. Alternatively you can have a # filename formatted as ``@``, in which case the ```` is # treated as local filesystem location and ```` as remote/virtual # filesystem location. The remote location can't be absolute or contain ``..`` # / ``@`` characters. # # Unless :variable:`CORRADE_TESTSUITE_TARGET_XCTEST` is set, test cases on iOS # targets are created as bundles with bundle identifier set to CMake project # name by default. Use the cache variable :variable:`CORRADE_TESTSUITE_BUNDLE_IDENTIFIER_PREFIX` # to change it to something else. # # .. command:: corrade_add_resource # # Compile data resources into application binary:: # # corrade_add_resource( ) # # Depends on ``Corrade::rc``, which is part of Corrade utilities. This command # generates resource data using given configuration file in current build # directory. Argument name is name under which the resources can be explicitly # loaded. Variable ```` contains compiled resource filename, which is # then used for compiling library / executable. On CMake >= 3.1 the # `resources.conf` file can contain UTF-8-encoded filenames. Example usage:: # # corrade_add_resource(app_resources resources.conf) # add_executable(app source1 source2 ... ${app_resources}) # # .. command:: corrade_add_plugin # # Add dynamic plugin:: # # corrade_add_plugin( # ";" # ";" # # ...) # # The macro adds preprocessor directive ``CORRADE_DYNAMIC_PLUGIN``. Additional # libraries can be linked in via :command:`target_link_libraries(plugin_name ...) `. # On DLL platforms, the plugin DLLs and metadata files are put into # ````/```` and the # ``*.lib`` files into ````/````. # On non-DLL platforms everything is put into ````/ # ````. # # corrade_add_plugin( # # # # ...) # # Unline the above version this puts everything into ```` on # both DLL and non-DLL platforms. If ```` is set to # :variable:`CMAKE_CURRENT_BINARY_DIR` (e.g. for testing purposes), the files # are copied directly, without the need to perform install step. Note that the # files are actually put into configuration-based subdirectory, i.e. # ``${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}``. See documentation of # :variable:`CMAKE_CFG_INTDIR` variable for more information. # # .. command:: corrade_add_static_plugin # # Add static plugin:: # # corrade_add_static_plugin( # ";" # # ...) # # The macro adds preprocessor directive ``CORRADE_STATIC_PLUGIN``. Additional # libraries can be linked in via :command:`target_link_libraries(plugin_name ...) `. # The ```` is ignored and included just for compatibility # with the :command:`corrade_add_plugin` command, everything is installed into # ````. Note that plugins built in debug configuration # (e.g. with :variable:`CMAKE_BUILD_TYPE` set to ``Debug``) have ``"-d"`` # suffix to make it possible to have both debug and release plugins installed # alongside each other. # # corrade_add_static_plugin( # # # ...) # # Equivalent to the above with ```` set to ````. # If ```` is set to :variable:`CMAKE_CURRENT_BINARY_DIR` (e.g. for # testing purposes), no installation rules are added. # # .. command:: corrade_find_dlls_for_libs # # Find corresponding DLLs for library files:: # # corrade_find_dlls_for_libs( ...) # # Available only on Windows, for all ``*.lib`` files tries to find # corresponding DLL file. Useful for bundling dependencies for e.g. WinRT # packages. # # # This file is part of Corrade. # # Copyright © 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, # 2017, 2018, 2019 Vladimír Vondruš # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # Root include dir find_path(CORRADE_INCLUDE_DIR NAMES Corrade/Corrade.h) mark_as_advanced(CORRADE_INCLUDE_DIR) # Configuration file find_file(_CORRADE_CONFIGURE_FILE configure.h HINTS ${CORRADE_INCLUDE_DIR}/Corrade/) mark_as_advanced(_CORRADE_CONFIGURE_FILE) # We need to open configure.h file from CORRADE_INCLUDE_DIR before we check for # the components. Bail out with proper error message if it wasn't found. The # complete check with all components is further below. if(NOT CORRADE_INCLUDE_DIR) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Corrade REQUIRED_VARS CORRADE_INCLUDE_DIR _CORRADE_CONFIGURE_FILE) endif() # Read flags from configuration file(READ ${_CORRADE_CONFIGURE_FILE} _corradeConfigure) set(_corradeFlags # WARNING: CAREFUL HERE, the string(FIND) succeeds even if a subset is # found -- so e.g. looking for TARGET_GL will match TARGET_GLES2 as well. # So far that's not a problem, but might become an issue for new flags. MSVC2015_COMPATIBILITY MSVC2017_COMPATIBILITY MSVC2019_COMPATIBILITY BUILD_DEPRECATED BUILD_STATIC BUILD_MULTITHREADED TARGET_UNIX TARGET_APPLE TARGET_IOS TARGET_IOS_SIMULATOR TARGET_WINDOWS TARGET_WINDOWS_RT TARGET_EMSCRIPTEN TARGET_ANDROID PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT TESTSUITE_TARGET_XCTEST UTILITY_USE_ANSI_COLORS) foreach(_corradeFlag ${_corradeFlags}) string(FIND "${_corradeConfigure}" "#define CORRADE_${_corradeFlag}" _corrade_${_corradeFlag}) if(NOT _corrade_${_corradeFlag} EQUAL -1) set(CORRADE_${_corradeFlag} 1) endif() endforeach() # CMake module dir find_path(_CORRADE_MODULE_DIR NAMES UseCorrade.cmake CorradeLibSuffix.cmake PATH_SUFFIXES share/cmake/Corrade) mark_as_advanced(_CORRADE_MODULE_DIR) set(CORRADE_USE_MODULE ${_CORRADE_MODULE_DIR}/UseCorrade.cmake) set(CORRADE_LIB_SUFFIX_MODULE ${_CORRADE_MODULE_DIR}/CorradeLibSuffix.cmake) # Ensure that all inter-component dependencies are specified as well foreach(_component ${Corrade_FIND_COMPONENTS}) string(TOUPPER ${_component} _COMPONENT) if(_component STREQUAL Containers) set(_CORRADE_${_COMPONENT}_DEPENDENCIES Utility) elseif(_component STREQUAL Interconnect) set(_CORRADE_${_COMPONENT}_DEPENDENCIES Utility) elseif(_component STREQUAL PluginManager) set(_CORRADE_${_COMPONENT}_DEPENDENCIES Containers Utility rc) elseif(_component STREQUAL TestSuite) set(_CORRADE_${_COMPONENT}_DEPENDENCIES Utility Main) # see below elseif(_component STREQUAL Utility) set(_CORRADE_${_COMPONENT}_DEPENDENCIES Containers rc) endif() # Mark the dependencies as required if the component is also required if(Corrade_FIND_REQUIRED_${_component}) foreach(_dependency ${_CORRADE_${_COMPONENT}_DEPENDENCIES}) set(Corrade_FIND_REQUIRED_${_dependency} TRUE) endforeach() endif() list(APPEND _CORRADE_ADDITIONAL_COMPONENTS ${_CORRADE_${_COMPONENT}_DEPENDENCIES}) # Main is linked only in corrade_add_test(), not to everything that depends # on TestSuite, so remove it from the list again once we filled the above # variables if(_component STREQUAL TestSuite) set(_CORRADE_${_COMPONENT}_DEPENDENCIES Utility) endif() endforeach() # Join the lists, remove duplicate components if(_CORRADE_ADDITIONAL_COMPONENTS) list(INSERT Corrade_FIND_COMPONENTS 0 ${_CORRADE_ADDITIONAL_COMPONENTS}) endif() if(Corrade_FIND_COMPONENTS) list(REMOVE_DUPLICATES Corrade_FIND_COMPONENTS) endif() # Component distinction set(_CORRADE_LIBRARY_COMPONENTS "^(Containers|Interconnect|Main|PluginManager|TestSuite|Utility)$") if(CORRADE_TARGET_WINDOWS) # CorradeMain is a real library only on windows, a dummy target elsewhere set(_CORRADE_HEADER_ONLY_COMPONENTS "^(Containers)$") else() set(_CORRADE_HEADER_ONLY_COMPONENTS "^(Containers|Main)$") endif() set(_CORRADE_EXECUTABLE_COMPONENTS "^(rc)$") # Find all components foreach(_component ${Corrade_FIND_COMPONENTS}) string(TOUPPER ${_component} _COMPONENT) # Create imported target in case the library is found. If the project is # added as subproject to CMake, the target already exists and all the # required setup is already done from the build tree. if(TARGET Corrade::${_component}) set(Corrade_${_component}_FOUND TRUE) else() # Library (and not header-only) components if(_component MATCHES ${_CORRADE_LIBRARY_COMPONENTS} AND NOT _component MATCHES ${_CORRADE_HEADER_ONLY_COMPONENTS}) add_library(Corrade::${_component} UNKNOWN IMPORTED) # Try to find both debug and release version find_library(CORRADE_${_COMPONENT}_LIBRARY_DEBUG Corrade${_component}-d) find_library(CORRADE_${_COMPONENT}_LIBRARY_RELEASE Corrade${_component}) mark_as_advanced(CORRADE_${_COMPONENT}_LIBRARY_DEBUG CORRADE_${_COMPONENT}_LIBRARY_RELEASE) if(CORRADE_${_COMPONENT}_LIBRARY_RELEASE) set_property(TARGET Corrade::${_component} APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_property(TARGET Corrade::${_component} PROPERTY IMPORTED_LOCATION_RELEASE ${CORRADE_${_COMPONENT}_LIBRARY_RELEASE}) endif() if(CORRADE_${_COMPONENT}_LIBRARY_DEBUG) set_property(TARGET Corrade::${_component} APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) set_property(TARGET Corrade::${_component} PROPERTY IMPORTED_LOCATION_DEBUG ${CORRADE_${_COMPONENT}_LIBRARY_DEBUG}) endif() endif() # Header-only library components if(_component MATCHES ${_CORRADE_HEADER_ONLY_COMPONENTS}) add_library(Corrade::${_component} INTERFACE IMPORTED) endif() # Default include path names to look for for library / header-only # components if(_component MATCHES ${_CORRADE_LIBRARY_COMPONENTS}) set(_CORRADE_${_COMPONENT}_INCLUDE_PATH_SUFFIX Corrade/${_component}) set(_CORRADE_${_COMPONENT}_INCLUDE_PATH_NAMES ${_component}.h) endif() # Executable components if(_component MATCHES ${_CORRADE_EXECUTABLE_COMPONENTS}) add_executable(Corrade::${_component} IMPORTED) find_program(CORRADE_${_COMPONENT}_EXECUTABLE corrade-${_component}) mark_as_advanced(CORRADE_${_COMPONENT}_EXECUTABLE) if(CORRADE_${_COMPONENT}_EXECUTABLE) set_property(TARGET Corrade::${_component} PROPERTY IMPORTED_LOCATION ${CORRADE_${_COMPONENT}_EXECUTABLE}) endif() endif() # No special setup for Containers library # Interconnect library if(_component STREQUAL Interconnect) # Disable /OPT:ICF on MSVC, which merges functions with identical # contents and thus breaks signal comparison if(CORRADE_TARGET_WINDOWS AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") if(CMAKE_VERSION VERSION_LESS 3.13) set_property(TARGET Corrade::${_component} PROPERTY INTERFACE_LINK_LIBRARIES "-OPT:NOICF,REF") else() set_property(TARGET Corrade::${_component} PROPERTY INTERFACE_LINK_OPTIONS "/OPT:NOICF,REF") endif() endif() # Main library elseif(_component STREQUAL Main) set(_CORRADE_${_COMPONENT}_INCLUDE_PATH_SUFFIX Corrade) set(_CORRADE_${_COMPONENT}_INCLUDE_PATH_NAMES Corrade.h) if(CORRADE_TARGET_WINDOWS) if(NOT MINGW) # Abusing INTERFACE_LINK_LIBRARIES because # INTERFACE_LINK_OPTIONS is only since 3.13. They treat # things with `-` in front as linker flags and fortunately # I can use `-ENTRY` instead of `/ENTRY`. # https://gitlab.kitware.com/cmake/cmake/issues/16543 set_property(TARGET Corrade::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-ENTRY:$<$>>:wmainCRTStartup>$<$>:wWinMainCRTStartup>") else() set_property(TARGET Corrade::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-municode") endif() endif() # PluginManager library elseif(_component STREQUAL PluginManager) # -ldl is handled by Utility now # TestSuite library has some additional files elseif(_component STREQUAL TestSuite) # XCTest runner file if(CORRADE_TESTSUITE_TARGET_XCTEST) find_file(CORRADE_TESTSUITE_XCTEST_RUNNER XCTestRunner.mm.in PATH_SUFFIXES share/corrade/TestSuite) set(CORRADE_TESTSUITE_XCTEST_RUNNER_NEEDED CORRADE_TESTSUITE_XCTEST_RUNNER) # ADB runner file elseif(CORRADE_TARGET_ANDROID) find_file(CORRADE_TESTSUITE_ADB_RUNNER AdbRunner.sh PATH_SUFFIXES share/corrade/TestSuite) set(CORRADE_TESTSUITE_ADB_RUNNER_NEEDED CORRADE_TESTSUITE_ADB_RUNNER) # Emscripten runner file elseif(CORRADE_TARGET_EMSCRIPTEN) find_file(CORRADE_TESTSUITE_EMSCRIPTEN_RUNNER EmscriptenRunner.html.in PATH_SUFFIXES share/corrade/TestSuite) set(CORRADE_TESTSUITE_EMSCRIPTEN_RUNNER_NEEDED CORRADE_TESTSUITE_EMSCRIPTEN_RUNNER) endif() # Utility library (contains all setup that is used by others) elseif(_component STREQUAL Utility) # Top-level include directory set_property(TARGET Corrade::${_component} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${CORRADE_INCLUDE_DIR}) # Require (at least) C++11 for users set_property(TARGET Corrade::${_component} PROPERTY INTERFACE_CORRADE_CXX_STANDARD 11) set_property(TARGET Corrade::${_component} APPEND PROPERTY COMPATIBLE_INTERFACE_NUMBER_MAX CORRADE_CXX_STANDARD) # Directory::libraryLocation() needs this if(CORRADE_TARGET_UNIX) set_property(TARGET Corrade::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS}) endif() # AndroidLogStreamBuffer class needs to be linked to log library if(CORRADE_TARGET_ANDROID) set_property(TARGET Corrade::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "log") endif() endif() # Find library includes if(_component MATCHES ${_CORRADE_LIBRARY_COMPONENTS}) find_path(_CORRADE_${_COMPONENT}_INCLUDE_DIR NAMES ${_CORRADE_${_COMPONENT}_INCLUDE_PATH_NAMES} HINTS ${CORRADE_INCLUDE_DIR}/${_CORRADE_${_COMPONENT}_INCLUDE_PATH_SUFFIX}) mark_as_advanced(_CORRADE_${_COMPONENT}_INCLUDE_DIR) endif() # Add inter-library dependencies if(_component MATCHES ${_CORRADE_LIBRARY_COMPONENTS} OR _component MATCHES ${_CORRADE_HEADER_ONLY_COMPONENTS}) foreach(_dependency ${_CORRADE_${_COMPONENT}_DEPENDENCIES}) if(_dependency MATCHES ${_CORRADE_LIBRARY_COMPONENTS} OR _dependency MATCHES ${_CORRADE_HEADER_ONLY_COMPONENTS}) set_property(TARGET Corrade::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Corrade::${_dependency}) endif() endforeach() endif() # Decide if the component was found if((_component MATCHES ${_CORRADE_LIBRARY_COMPONENTS} AND _CORRADE_${_COMPONENT}_INCLUDE_DIR AND (_component MATCHES ${_CORRADE_HEADER_ONLY_COMPONENTS} OR CORRADE_${_COMPONENT}_LIBRARY_RELEASE OR CORRADE_${_COMPONENT}_LIBRARY_DEBUG)) OR (_component MATCHES ${_CORRADE_EXECUTABLE_COMPONENTS} AND CORRADE_${_COMPONENT}_EXECUTABLE)) set(Corrade_${_component}_FOUND TRUE) else() set(Corrade_${_component}_FOUND FALSE) endif() endif() endforeach() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Corrade REQUIRED_VARS CORRADE_INCLUDE_DIR _CORRADE_MODULE_DIR _CORRADE_CONFIGURE_FILE ${CORRADE_TESTSUITE_XCTEST_RUNNER_NEEDED} ${CORRADE_TESTSUITE_ADB_RUNNER_NEEDED} ${CORRADE_TESTSUITE_EMSCRIPTEN_RUNNER_NEEDED} HANDLE_COMPONENTS) # Finalize the finding process include(${CORRADE_USE_MODULE}) # Installation dirs set(CORRADE_INCLUDE_INSTALL_PREFIX "." CACHE STRING "Prefix where to put platform-independent include and other files") set(CORRADE_INCLUDE_INSTALL_DIR ${CORRADE_INCLUDE_INSTALL_PREFIX}/include/Corrade) ================================================ FILE: src/cmake/modules/FindEGL.cmake ================================================ #.rst: # Find EGL # -------- # # Finds the EGL library. This module defines: # # EGL_FOUND - True if EGL library is found # EGL::EGL - EGL imported target # # Additionally these variables are defined for internal usage: # # EGL_LIBRARY - EGL library # EGL_INCLUDE_DIR - Include dir # # # This file is part of Magnum. # # Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, # 2020 Vladimír Vondruš # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # Under Emscripten, GL is linked implicitly. With MINIMAL_RUNTIME you need to # specify -lGL. Simply set the library name to that. if(CORRADE_TARGET_EMSCRIPTEN) set(EGL_LIBRARY GL CACHE STRING "Path to a library." FORCE) else() find_library(EGL_LIBRARY NAMES EGL # ANGLE (CMake doesn't search for lib prefix on Windows) libEGL # On iOS a part of OpenGLES OpenGLES) endif() # Include dir find_path(EGL_INCLUDE_DIR NAMES EGL/egl.h # iOS EAGL.h) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(EGL DEFAULT_MSG EGL_LIBRARY EGL_INCLUDE_DIR) if(NOT TARGET EGL::EGL) # Work around BUGGY framework support on macOS. Do this also in case of # Emscripten, since there we don't have a location either. # http://public.kitware.com/pipermail/cmake/2016-April/063179.html if((APPLE AND ${EGL_LIBRARY} MATCHES "\\.framework$") OR CORRADE_TARGET_EMSCRIPTEN) add_library(EGL::EGL INTERFACE IMPORTED) set_property(TARGET EGL::EGL APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${EGL_LIBRARY}) else() add_library(EGL::EGL UNKNOWN IMPORTED) set_property(TARGET EGL::EGL PROPERTY IMPORTED_LOCATION ${EGL_LIBRARY}) endif() set_target_properties(EGL::EGL PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${EGL_INCLUDE_DIR}) endif() mark_as_advanced(EGL_LIBRARY EGL_INCLUDE_DIR) ================================================ FILE: src/cmake/modules/FindMagnum.cmake ================================================ #.rst: # Find Magnum # ----------- # # Finds the Magnum library. Basic usage:: # # find_package(Magnum REQUIRED) # # This module tries to find the base Magnum library and then defines the # following: # # Magnum_FOUND - Whether the base library was found # MAGNUM_DEPLOY_PREFIX - Prefix where to put final application # executables, defaults to ``.``. If a relative path is used, it's relative # to :variable:`CMAKE_INSTALL_PREFIX`. # MAGNUM_INCLUDE_INSTALL_PREFIX - Prefix where to put platform-independent # include and other files, defaults to ``.``. If a relative path is used, # it's relative to :variable:`CMAKE_INSTALL_PREFIX`. # MAGNUM_PLUGINS_DEBUG_DIR - Base directory with dynamic plugins for # debug builds, defaults to magnum-d/ subdirectory of dir where Magnum # library was found # MAGNUM_PLUGINS_RELEASE_DIR - Base directory with dynamic plugins for # release builds, defaults to magnum/ subdirectory of dir where Magnum # library was found # MAGNUM_PLUGINS_DIR - Base directory with dynamic plugins, defaults # to :variable:`MAGNUM_PLUGINS_RELEASE_DIR` in release builds and # multi-configuration builds or to :variable:`MAGNUM_PLUGINS_DEBUG_DIR` in # debug builds # MAGNUM_PLUGINS_FONT[|_DEBUG|_RELEASE]_DIR - Directory with dynamic font # plugins # MAGNUM_PLUGINS_FONTCONVERTER[|_DEBUG|_RELEASE]_DIR - Directory with dynamic # font converter plugins # MAGNUM_PLUGINS_IMAGECONVERTER[|_DEBUG|_RELEASE]_DIR - Directory with dynamic # image converter plugins # MAGNUM_PLUGINS_IMPORTER[|_DEBUG|_RELEASE]_DIR - Directory with dynamic # importer plugins # MAGNUM_PLUGINS_AUDIOIMPORTER[|_DEBUG|_RELEASE]_DIR - Directory with dynamic # audio importer plugins # # If Magnum is built for Emscripten, the following variables contain paths to # various support files: # # MAGNUM_EMSCRIPTENAPPLICATION_JS - Path to the EmscriptenApplication.js file # MAGNUM_WINDOWLESSEMSCRIPTENAPPLICATION_JS - Path to the # WindowlessEmscriptenApplication.js file # MAGNUM_WEBAPPLICATION_CSS - Path to the WebApplication.css file # # This command will try to find only the base library, not the optional # components. The base library depends on Corrade and OpenGL libraries (or # OpenGL ES libraries). Additional dependencies are specified by the # components. The optional components are: # # AnyAudioImporter - Any audio importer # AnyImageConverter - Any image converter # AnyImageImporter - Any image importer # AnySceneImporter - Any scene importer # Audio - Audio library # DebugTools - DebugTools library # GL - GL library # MeshTools - MeshTools library # Primitives - Primitives library # SceneGraph - SceneGraph library # Shaders - Shaders library # Text - Text library # TextureTools - TextureTools library # Trade - Trade library # Vk - Vk library # AndroidApplication - Android application # EmscriptenApplication - Emscripten application # GlfwApplication - GLFW application # GlxApplication - GLX application # Sdl2Application - SDL2 application # XEglApplication - X/EGL application # WindowlessCglApplication - Windowless CGL application # WindowlessEglApplication - Windowless EGL application # WindowlessGlxApplication - Windowless GLX application # WindowlessIosApplication - Windowless iOS application # WindowlessWglApplication - Windowless WGL application # WindowlessWindowsEglApplication - Windowless Windows/EGL application # CglContext - CGL context # EglContext - EGL context # GlxContext - GLX context # WglContext - WGL context # OpenGLTester - OpenGLTester class # MagnumFont - Magnum bitmap font plugin # MagnumFontConverter - Magnum bitmap font converter plugin # ObjImporter - OBJ importer plugin # TgaImageConverter - TGA image converter plugin # TgaImporter - TGA importer plugin # WavAudioImporter - WAV audio importer plugin # distancefieldconverter - magnum-distancefieldconverter executable # fontconverter - magnum-fontconverter executable # imageconverter - magnum-imageconverter executable # gl-info - magnum-gl-info executable # al-info - magnum-al-info executable # # Example usage with specifying additional components is:: # # find_package(Magnum REQUIRED Trade MeshTools Primitives GlfwApplication) # # For each component is then defined: # # Magnum_*_FOUND - Whether the component was found # Magnum::* - Component imported target # # If exactly one ``*Application`` or exactly one ``Windowless*Application`` # component is requested and found, its target is available in convenience # alias ``Magnum::Application`` / ``Magnum::WindowlessApplication`` to simplify # porting. Similarly, if exactly one ``*Context`` component is requested and # found, its target is available in convenience alias ``Magnum::GLContext``. # # The package is found if either debug or release version of each requested # library (or plugin) is found. If both debug and release libraries (or # plugins) are found, proper version is chosen based on actual build # configuration of the project (i.e. Debug build is linked to debug libraries, # Release build to release libraries). Note that this autodetection might fail # for the :variable:`MAGNUM_PLUGINS_DIR` variable, especially on # multi-configuration build systems. You can make use of # ``CORRADE_IS_DEBUG_BUILD`` preprocessor variable along with # ``MAGNUM_PLUGINS_*_DEBUG_DIR`` / ``MAGNUM_PLUGINS_*_RELEASE_DIR`` variables # to decide in preprocessing step. # # Features of found Magnum library are exposed in these variables: # # MAGNUM_BUILD_DEPRECATED - Defined if compiled with deprecated APIs # included # MAGNUM_BUILD_STATIC - Defined if compiled as static libraries # MAGNUM_TARGET_GL - Defined if compiled with OpenGL interop # MAGNUM_TARGET_GLES - Defined if compiled for OpenGL ES # MAGNUM_TARGET_GLES2 - Defined if compiled for OpenGL ES 2.0 # MAGNUM_TARGET_GLES3 - Defined if compiled for OpenGL ES 3.0 # MAGNUM_TARGET_DESKTOP_GLES - Defined if compiled with OpenGL ES # emulation on desktop OpenGL # MAGNUM_TARGET_WEBGL - Defined if compiled for WebGL # MAGNUM_TARGET_HEADLESS - Defined if compiled for headless machines # MAGNUM_TARGET_VK - Defined if compiled with Vulkan interop # # The following variables are provided for backwards compatibility purposes # only when MAGNUM_BUILD_DEPRECATED is enabled and will be removed in a future # release: # # MAGNUM_BUILD_MULTITHREADED - Alias to CORRADE_BUILD_MULTITHREADED. Use # CORRADE_BUILD_MULTITHREADED instead. # # Additionally these variables are defined for internal usage: # # MAGNUM_INCLUDE_DIR - Root include dir (w/o dependencies) # MAGNUM_LIBRARY - Magnum library (w/o dependencies) # MAGNUM_LIBRARY_DEBUG - Debug version of Magnum library, if found # MAGNUM_LIBRARY_RELEASE - Release version of Magnum library, if found # MAGNUM_*_LIBRARY - Component libraries (w/o dependencies) # MAGNUM_*_LIBRARY_DEBUG - Debug version of given library, if found # MAGNUM_*_LIBRARY_RELEASE - Release version of given library, if found # MAGNUM_BINARY_INSTALL_DIR - Binary installation directory # MAGNUM_LIBRARY_INSTALL_DIR - Library installation directory # MAGNUM_DATA_INSTALL_DIR - Data installation directory # MAGNUM_PLUGINS_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Plugin binary # installation directory # MAGNUM_PLUGINS_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Plugin library # installation directory # MAGNUM_PLUGINS_FONT_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Font plugin binary # installation directory # MAGNUM_PLUGINS_FONT_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Font plugin # library installation directory # MAGNUM_PLUGINS_FONTCONVERTER_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Font # converter plugin binary installation directory # MAGNUM_PLUGINS_FONTCONVERTER_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Font # converter plugin library installation directory # MAGNUM_PLUGINS_IMAGECONVERTER_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Image # converter plugin binary installation directory # MAGNUM_PLUGINS_IMAGECONVERTER_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Image # converter plugin library installation directory # MAGNUM_PLUGINS_IMPORTER_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Importer # plugin binary installation directory # MAGNUM_PLUGINS_IMPORTER_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Importer # plugin library installation directory # MAGNUM_PLUGINS_AUDIOIMPORTER_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Audio # importer plugin binary installation directory # MAGNUM_PLUGINS_AUDIOIMPORTER_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Audio # importer plugin library installation directory # MAGNUM_INCLUDE_INSTALL_DIR - Header installation directory # MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR - Plugin header installation directory # # # This file is part of Magnum. # # Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 # Vladimír Vondruš # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # Corrade library dependencies set(_MAGNUM_CORRADE_DEPENDENCIES ) foreach(_component ${Magnum_FIND_COMPONENTS}) string(TOUPPER ${_component} _COMPONENT) # Unrolling the transitive dependencies here so this doesn't need to be # after resolving inter-component dependencies. Listing also all plugins. if(_component MATCHES "^(Audio|DebugTools|MeshTools|Primitives|Text|TextureTools|Trade|.+Importer|.+ImageConverter|.+Font)$") set(_MAGNUM_${_COMPONENT}_CORRADE_DEPENDENCIES PluginManager) endif() list(APPEND _MAGNUM_CORRADE_DEPENDENCIES ${_MAGNUM_${_COMPONENT}_CORRADE_DEPENDENCIES}) endforeach() find_package(Corrade REQUIRED Utility ${_MAGNUM_CORRADE_DEPENDENCIES}) # Root include dir find_path(MAGNUM_INCLUDE_DIR NAMES Magnum/Magnum.h) mark_as_advanced(MAGNUM_INCLUDE_DIR) # Configuration file find_file(_MAGNUM_CONFIGURE_FILE configure.h HINTS ${MAGNUM_INCLUDE_DIR}/Magnum/) mark_as_advanced(_MAGNUM_CONFIGURE_FILE) # We need to open configure.h file from MAGNUM_INCLUDE_DIR before we check for # the components. Bail out with proper error message if it wasn't found. The # complete check with all components is further below. if(NOT MAGNUM_INCLUDE_DIR) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Magnum REQUIRED_VARS MAGNUM_INCLUDE_DIR _MAGNUM_CONFIGURE_FILE) endif() # Read flags from configuration file(READ ${_MAGNUM_CONFIGURE_FILE} _magnumConfigure) set(_magnumFlags # WARNING: CAREFUL HERE, the string(FIND) succeeds even if a subset is # found -- so e.g. looking for TARGET_GL will match TARGET_GLES2 as well. # So far that's not a problem, but might become an issue for new flags. BUILD_DEPRECATED BUILD_STATIC TARGET_GL TARGET_GLES TARGET_GLES2 TARGET_GLES3 TARGET_DESKTOP_GLES TARGET_WEBGL TARGET_HEADLESS TARGET_VK) foreach(_magnumFlag ${_magnumFlags}) string(FIND "${_magnumConfigure}" "#define MAGNUM_${_magnumFlag}" _magnum_${_magnumFlag}) if(NOT _magnum_${_magnumFlag} EQUAL -1) set(MAGNUM_${_magnumFlag} 1) endif() endforeach() # For compatibility only, to be removed at some point if(MAGNUM_BUILD_DEPRECATED AND CORRADE_BUILD_MULTITHREADED) set(MAGNUM_BUILD_MULTITHREADED 1) endif() # OpenGL library preference. Prefer to use GLVND, since that's the better # approach nowadays, but allow the users to override it from outside in case # it is broken for some reason (Nvidia drivers in Debian's testing (Buster) -- # reported on 2019-04-09). if(NOT CMAKE_VERSION VERSION_LESS 3.10 AND NOT OpenGL_GL_PREFERENCE) set(OpenGL_GL_PREFERENCE GLVND) endif() # Base Magnum library if(NOT TARGET Magnum::Magnum) add_library(Magnum::Magnum UNKNOWN IMPORTED) # Try to find both debug and release version find_library(MAGNUM_LIBRARY_DEBUG Magnum-d) find_library(MAGNUM_LIBRARY_RELEASE Magnum) mark_as_advanced(MAGNUM_LIBRARY_DEBUG MAGNUM_LIBRARY_RELEASE) # Set the MAGNUM_LIBRARY variable based on what was found, use that # information to guess also build type of dynamic plugins if(MAGNUM_LIBRARY_DEBUG AND MAGNUM_LIBRARY_RELEASE) set(MAGNUM_LIBRARY ${MAGNUM_LIBRARY_RELEASE}) get_filename_component(_MAGNUM_PLUGINS_DIR_PREFIX ${MAGNUM_LIBRARY_DEBUG} PATH) if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(_MAGNUM_PLUGINS_DIR_SUFFIX "-d") endif() elseif(MAGNUM_LIBRARY_DEBUG) set(MAGNUM_LIBRARY ${MAGNUM_LIBRARY_DEBUG}) get_filename_component(_MAGNUM_PLUGINS_DIR_PREFIX ${MAGNUM_LIBRARY_DEBUG} PATH) set(_MAGNUM_PLUGINS_DIR_SUFFIX "-d") elseif(MAGNUM_LIBRARY_RELEASE) set(MAGNUM_LIBRARY ${MAGNUM_LIBRARY_RELEASE}) get_filename_component(_MAGNUM_PLUGINS_DIR_PREFIX ${MAGNUM_LIBRARY_RELEASE} PATH) endif() # On DLL platforms the plugins are stored in bin/ instead of lib/, modify # _MAGNUM_PLUGINS_DIR_PREFIX accordingly if(CORRADE_TARGET_WINDOWS) get_filename_component(_MAGNUM_PLUGINS_DIR_PREFIX ${_MAGNUM_PLUGINS_DIR_PREFIX} PATH) set(_MAGNUM_PLUGINS_DIR_PREFIX ${_MAGNUM_PLUGINS_DIR_PREFIX}/bin) endif() if(MAGNUM_LIBRARY_RELEASE) set_property(TARGET Magnum::Magnum APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_property(TARGET Magnum::Magnum PROPERTY IMPORTED_LOCATION_RELEASE ${MAGNUM_LIBRARY_RELEASE}) endif() if(MAGNUM_LIBRARY_DEBUG) set_property(TARGET Magnum::Magnum APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) set_property(TARGET Magnum::Magnum PROPERTY IMPORTED_LOCATION_DEBUG ${MAGNUM_LIBRARY_DEBUG}) endif() # Include directories set_property(TARGET Magnum::Magnum APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MAGNUM_INCLUDE_DIR}) # Some deprecated APIs use headers (but not externally defined symbols) # from the GL library, link those includes as well if(MAGNUM_BUILD_DEPRECATED AND MAGNUM_TARGET_GL) set_property(TARGET Magnum::Magnum APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${MAGNUM_INCLUDE_DIR}/MagnumExternal/OpenGL) endif() # Dependent libraries set_property(TARGET Magnum::Magnum APPEND PROPERTY INTERFACE_LINK_LIBRARIES Corrade::Utility) else() set(MAGNUM_LIBRARY Magnum::Magnum) endif() # Component distinction (listing them explicitly to avoid mistakes with finding # components from other repositories) set(_MAGNUM_LIBRARY_COMPONENT_LIST Audio DebugTools GL MeshTools Primitives SceneGraph Shaders Text TextureTools Trade Vk AndroidApplication EmscriptenApplication GlfwApplication GlxApplication Sdl2Application XEglApplication WindowlessCglApplication WindowlessEglApplication WindowlessGlxApplication WindowlessIosApplication WindowlessWglApplication WindowlessWindowsEglApplication CglContext EglContext GlxContext WglContext OpenGLTester) set(_MAGNUM_PLUGIN_COMPONENT_LIST AnyAudioImporter AnyImageConverter AnyImageImporter AnySceneImporter MagnumFont MagnumFontConverter ObjImporter TgaImageConverter TgaImporter WavAudioImporter) set(_MAGNUM_EXECUTABLE_COMPONENT_LIST distancefieldconverter fontconverter imageconverter gl-info al-info) # Inter-component dependencies set(_MAGNUM_Audio_DEPENDENCIES ) set(_MAGNUM_DebugTools_DEPENDENCIES ) if(MAGNUM_TARGET_GL) # MeshTools, Primitives, SceneGraph and Shaders are used only for GL # renderers. All of this is optional, compiled in only if the base library # was selected. list(APPEND _MAGNUM_DebugTools_DEPENDENCIES MeshTools Primitives SceneGraph Shaders Trade GL) set(_MAGNUM_DebugTools_MeshTools_DEPENDENCY_IS_OPTIONAL ON) set(_MAGNUM_DebugTools_Primitives_DEPENDENCY_IS_OPTIONAL ON) set(_MAGNUM_DebugTools_SceneGraph_DEPENDENCY_IS_OPTIONAL ON) set(_MAGNUM_DebugTools_Shaders_DEPENDENCY_IS_OPTIONAL ON) set(_MAGNUM_DebugTools_Trade_DEPENDENCY_IS_OPTIONAL ON) set(_MAGNUM_DebugTools_GL_DEPENDENCY_IS_OPTIONAL ON) endif() set(_MAGNUM_MeshTools_DEPENDENCIES ) if(MAGNUM_TARGET_GL) # Trade is used only in compile(), which needs GL as well list(APPEND _MAGNUM_MeshTools_DEPENDENCIES Trade GL) endif() set(_MAGNUM_OpenGLTester_DEPENDENCIES GL) if(MAGNUM_TARGET_HEADLESS OR CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessEglApplication) elseif(CORRADE_TARGET_IOS) list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessIosApplication) elseif(CORRADE_TARGET_APPLE) list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessCglApplication) elseif(CORRADE_TARGET_UNIX) if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES) list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessEglApplication) else() list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessGlxApplication) endif() elseif(CORRADE_TARGET_WINDOWS) if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES) list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessWglApplication) else() list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessWindowsEglApplication) endif() endif() set(_MAGNUM_Primitives_DEPENDENCIES Trade) set(_MAGNUM_SceneGraph_DEPENDENCIES ) set(_MAGNUM_Shaders_DEPENDENCIES GL) set(_MAGNUM_Text_DEPENDENCIES TextureTools) if(MAGNUM_TARGET_GL) list(APPEND _MAGNUM_Text_DEPENDENCIES GL) endif() set(_MAGNUM_TextureTools_DEPENDENCIES ) if(MAGNUM_TARGET_GL) list(APPEND _MAGNUM_TextureTools_DEPENDENCIES GL) endif() set(_MAGNUM_Trade_DEPENDENCIES ) set(_MAGNUM_AndroidApplication_DEPENDENCIES GL) set(_MAGNUM_EmscriptenApplication_DEPENDENCIES) if(MAGNUM_TARGET_GL) list(APPEND _MAGNUM_EmscriptenApplication_DEPENDENCIES GL) endif() set(_MAGNUM_GlfwApplication_DEPENDENCIES ) if(MAGNUM_TARGET_GL) list(APPEND _MAGNUM_GlfwApplication_DEPENDENCIES GL) endif() set(_MAGNUM_GlxApplication_DEPENDENCIES GL) set(_MAGNUM_Sdl2Application_DEPENDENCIES ) if(MAGNUM_TARGET_GL) list(APPEND _MAGNUM_Sdl2Application_DEPENDENCIES GL) endif() set(_MAGNUM_WindowlessCglApplication_DEPENDENCIES GL) set(_MAGNUM_WindowlessEglApplication_DEPENDENCIES GL) set(_MAGNUM_WindowlessGlxApplication_DEPENDENCIES GL) set(_MAGNUM_WindowlessIosApplication_DEPENDENCIES GL) set(_MAGNUM_WindowlessWglApplication_DEPENDENCIES GL) set(_MAGNUM_WindowlessWindowsEglApplication_DEPENDENCIES GL) set(_MAGNUM_XEglApplication_DEPENDENCIES GL) set(_MAGNUM_CglContext_DEPENDENCIES GL) set(_MAGNUM_EglContext_DEPENDENCIES GL) set(_MAGNUM_GlxContext_DEPENDENCIES GL) set(_MAGNUM_WglContext_DEPENDENCIES GL) set(_MAGNUM_MagnumFont_DEPENDENCIES Trade TgaImporter GL) # and below set(_MAGNUM_MagnumFontConverter_DEPENDENCIES Trade TgaImageConverter) # and below set(_MAGNUM_ObjImporter_DEPENDENCIES MeshTools) # and below foreach(_component ${_MAGNUM_PLUGIN_COMPONENT_LIST}) if(_component MATCHES ".+AudioImporter") list(APPEND _MAGNUM_${_component}_DEPENDENCIES Audio) elseif(_component MATCHES ".+(Importer|ImageConverter)") list(APPEND _MAGNUM_${_component}_DEPENDENCIES Trade) elseif(_component MATCHES ".+(Font|FontConverter)") list(APPEND _MAGNUM_${_component}_DEPENDENCIES Text TextureTools) endif() endforeach() # Ensure that all inter-component dependencies are specified as well set(_MAGNUM_ADDITIONAL_COMPONENTS ) foreach(_component ${Magnum_FIND_COMPONENTS}) # Mark the dependencies as required if the component is also required, but # only if they themselves are not optional (for example parts of DebugTools # are present only if their respective base library is compiled) if(Magnum_FIND_REQUIRED_${_component}) foreach(_dependency ${_MAGNUM_${_component}_DEPENDENCIES}) if(NOT _MAGNUM_${_component}_${_dependency}_DEPENDENCY_IS_OPTIONAL) set(Magnum_FIND_REQUIRED_${_dependency} TRUE) endif() endforeach() endif() list(APPEND _MAGNUM_ADDITIONAL_COMPONENTS ${_MAGNUM_${_component}_DEPENDENCIES}) endforeach() # Join the lists, remove duplicate components if(_MAGNUM_ADDITIONAL_COMPONENTS) list(INSERT Magnum_FIND_COMPONENTS 0 ${_MAGNUM_ADDITIONAL_COMPONENTS}) endif() if(Magnum_FIND_COMPONENTS) list(REMOVE_DUPLICATES Magnum_FIND_COMPONENTS) endif() # Convert components lists to regular expressions so I can use if(MATCHES). # TODO: Drop this once CMake 3.3 and if(IN_LIST) can be used foreach(_WHAT LIBRARY PLUGIN EXECUTABLE) string(REPLACE ";" "|" _MAGNUM_${_WHAT}_COMPONENTS "${_MAGNUM_${_WHAT}_COMPONENT_LIST}") set(_MAGNUM_${_WHAT}_COMPONENTS "^(${_MAGNUM_${_WHAT}_COMPONENTS})$") endforeach() # Find all components. Maintain a list of components that'll need to have # their optional dependencies checked. set(_MAGNUM_OPTIONAL_DEPENDENCIES_TO_ADD ) foreach(_component ${Magnum_FIND_COMPONENTS}) string(TOUPPER ${_component} _COMPONENT) # Create imported target in case the library is found. If the project is # added as subproject to CMake, the target already exists and all the # required setup is already done from the build tree. if(TARGET Magnum::${_component}) set(Magnum_${_component}_FOUND TRUE) else() # Library components if(_component MATCHES ${_MAGNUM_LIBRARY_COMPONENTS}) add_library(Magnum::${_component} UNKNOWN IMPORTED) # Set library defaults, find the library set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/${_component}) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES ${_component}.h) # Try to find both debug and release version find_library(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG Magnum${_component}-d) find_library(MAGNUM_${_COMPONENT}_LIBRARY_RELEASE Magnum${_component}) mark_as_advanced(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG MAGNUM_${_COMPONENT}_LIBRARY_RELEASE) endif() # Plugin components if(_component MATCHES ${_MAGNUM_PLUGIN_COMPONENTS}) add_library(Magnum::${_component} UNKNOWN IMPORTED) # AudioImporter plugin specific name suffixes if(_component MATCHES ".+AudioImporter$") set(_MAGNUM_${_COMPONENT}_PATH_SUFFIX audioimporters) # Audio importer class is Audio::*Importer, thus we need to # convert *AudioImporter.h to *Importer.h string(REPLACE "AudioImporter" "Importer" _MAGNUM_${_COMPONENT}_HEADER_NAME "${_component}") set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES ${_MAGNUM_${_COMPONENT}_HEADER_NAME}.h) # Importer plugin specific name suffixes elseif(_component MATCHES ".+Importer$") set(_MAGNUM_${_COMPONENT}_PATH_SUFFIX importers) # Font plugin specific name suffixes elseif(_component MATCHES ".+Font$") set(_MAGNUM_${_COMPONENT}_PATH_SUFFIX fonts) # ImageConverter plugin specific name suffixes elseif(_component MATCHES ".+ImageConverter$") set(_MAGNUM_${_COMPONENT}_PATH_SUFFIX imageconverters) # FontConverter plugin specific name suffixes elseif(_component MATCHES ".+FontConverter$") set(_MAGNUM_${_COMPONENT}_PATH_SUFFIX fontconverters) endif() # Don't override the exception for *AudioImporter plugins set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX MagnumPlugins/${_component}) if(NOT _MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES ${_component}.h) endif() # Dynamic plugins don't have any prefix (e.g. `lib` on Linux), # search with empty prefix and then reset that back so we don't # accidentaly break something else set(_tmp_prefixes "${CMAKE_FIND_LIBRARY_PREFIXES}") set(CMAKE_FIND_LIBRARY_PREFIXES "${CMAKE_FIND_LIBRARY_PREFIXES};") # Try to find both debug and release version. Dynamic and static # debug libraries are in different places. Static debug plugins are # in magnum/ with a -d suffix while dynamic debug plugins are in # magnum-d/ with no suffix. Problem is that Vcpkg's library linking # automagic needs the static libs to be in the root library # directory along with everything else and so we need to search for # the -d suffixed version *before* the unsuffixed so it doesn't # pick the release library for both debug and release. find_library(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG ${_component}-d PATH_SUFFIXES magnum/${_MAGNUM_${_COMPONENT}_PATH_SUFFIX}) find_library(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG ${_component} PATH_SUFFIXES magnum-d/${_MAGNUM_${_COMPONENT}_PATH_SUFFIX}) find_library(MAGNUM_${_COMPONENT}_LIBRARY_RELEASE ${_component} PATH_SUFFIXES magnum/${_MAGNUM_${_COMPONENT}_PATH_SUFFIX}) mark_as_advanced(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG MAGNUM_${_COMPONENT}_LIBRARY_RELEASE) # Reset back set(CMAKE_FIND_LIBRARY_PREFIXES "${_tmp_prefixes}") endif() # Library location for libraries/plugins if(_component MATCHES ${_MAGNUM_LIBRARY_COMPONENTS} OR _component MATCHES ${_MAGNUM_PLUGIN_COMPONENTS}) if(MAGNUM_${_COMPONENT}_LIBRARY_RELEASE) set_property(TARGET Magnum::${_component} APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_property(TARGET Magnum::${_component} PROPERTY IMPORTED_LOCATION_RELEASE ${MAGNUM_${_COMPONENT}_LIBRARY_RELEASE}) endif() if(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG) set_property(TARGET Magnum::${_component} APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) set_property(TARGET Magnum::${_component} PROPERTY IMPORTED_LOCATION_DEBUG ${MAGNUM_${_COMPONENT}_LIBRARY_DEBUG}) endif() endif() # Executables if(_component MATCHES ${_MAGNUM_EXECUTABLE_COMPONENTS}) add_executable(Magnum::${_component} IMPORTED) find_program(MAGNUM_${_COMPONENT}_EXECUTABLE magnum-${_component}) mark_as_advanced(MAGNUM_${_COMPONENT}_EXECUTABLE) if(MAGNUM_${_COMPONENT}_EXECUTABLE) set_property(TARGET Magnum::${_component} PROPERTY IMPORTED_LOCATION ${MAGNUM_${_COMPONENT}_EXECUTABLE}) endif() endif() # Applications if(_component MATCHES ".+Application") set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/Platform) # Android application dependencies if(_component STREQUAL AndroidApplication) find_package(EGL) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES android EGL::EGL) # EmscriptenApplication has no additional dependencies # GLFW application dependencies elseif(_component STREQUAL GlfwApplication) find_package(GLFW) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES GLFW::GLFW) # Use the Foundation framework on Apple to query the DPI awareness if(CORRADE_TARGET_APPLE) find_library(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY Foundation) mark_as_advanced(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY}) # Needed for opt-in DPI queries elseif(CORRADE_TARGET_UNIX) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS}) endif() # With GLVND (since CMake 3.11) we need to explicitly link to # GLX/EGL because libOpenGL doesn't provide it. For EGL we have # our own EGL find module, which makes things simpler. The # upstream FindOpenGL is anything but simple. Also can't use # OpenGL_OpenGL_FOUND, because that one is set also if GLVND is # *not* found. WTF. Also can't just check for # OPENGL_opengl_LIBRARY because that's set even if # OpenGL_GL_PREFERENCE is explicitly set to LEGACY. if(MAGNUM_TARGET_GL) if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE AND (NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)) find_package(OpenGL) if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES OpenGL::GLX) endif() elseif(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES AND NOT CORRADE_TARGET_EMSCRIPTEN) find_package(EGL) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL) endif() endif() # SDL2 application dependencies elseif(_component STREQUAL Sdl2Application) find_package(SDL2) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES SDL2::SDL2) # Use the Foundation framework on Apple to query the DPI awareness if(CORRADE_TARGET_APPLE) find_library(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY Foundation) mark_as_advanced(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY}) # Needed for opt-in DPI queries elseif(CORRADE_TARGET_UNIX) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS}) endif() # With GLVND (since CMake 3.11) we need to explicitly link to # GLX/EGL because libOpenGL doesn't provide it. For EGL we have # our own EGL find module, which makes things simpler. The # upstream FindOpenGL is anything but simple. Also can't use # OpenGL_OpenGL_FOUND, because that one is set also if GLVND is # *not* found. WTF. Also can't just check for # OPENGL_opengl_LIBRARY because that's set even if # OpenGL_GL_PREFERENCE is explicitly set to LEGACY. if(MAGNUM_TARGET_GL) if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE AND (NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)) find_package(OpenGL) if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES OpenGL::GLX) endif() elseif(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES AND NOT CORRADE_TARGET_EMSCRIPTEN) find_package(EGL) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL) endif() endif() # (Windowless) GLX application dependencies elseif(_component STREQUAL GlxApplication OR _component STREQUAL WindowlessGlxApplication) find_package(X11) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${X11_INCLUDE_DIR}) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${X11_LIBRARIES}) # With GLVND (since CMake 3.11) we need to explicitly link to # GLX because libOpenGL doesn't provide it. Also can't use # OpenGL_OpenGL_FOUND, because that one is set also if GLVND is # *not* found. WTF. Also can't just check for # OPENGL_opengl_LIBRARY because that's set even if # OpenGL_GL_PREFERENCE is explicitly set to LEGACY. find_package(OpenGL) if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES OpenGL::GLX) endif() # Windowless CGL application has no additional dependencies # Windowless EGL application dependencies elseif(_component STREQUAL WindowlessEglApplication) find_package(EGL) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL) # Windowless iOS application dependencies elseif(_component STREQUAL WindowlessIosApplication) # We need to link to Foundation framework to use ObjC find_library(_MAGNUM_IOS_FOUNDATION_FRAMEWORK_LIBRARY Foundation) mark_as_advanced(_MAGNUM_IOS_FOUNDATION_FRAMEWORK_LIBRARY) find_package(EGL) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL ${_MAGNUM_IOS_FOUNDATION_FRAMEWORK_LIBRARY}) # Windowless WGL application has no additional dependencies # Windowless Windows/EGL application dependencies elseif(_component STREQUAL WindowlessWindowsEglApplication) find_package(EGL) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL) # X/EGL application dependencies elseif(_component STREQUAL XEglApplication) find_package(EGL) find_package(X11) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${X11_INCLUDE_DIR}) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL ${X11_LIBRARIES}) endif() # Context libraries elseif(_component MATCHES ".+Context") set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/Platform) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES GLContext.h) # GLX context dependencies if(_component STREQUAL GlxContext) # With GLVND (since CMake 3.11) we need to explicitly link to # GLX because libOpenGL doesn't provide it. Also can't use # OpenGL_OpenGL_FOUND, because that one is set also if GLVND is # *not* found. If GLVND is not used, link to X11 instead. Also # can't just check for OPENGL_opengl_LIBRARY because that's set # even if OpenGL_GL_PREFERENCE is explicitly set to LEGACY. find_package(OpenGL) if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES OpenGL::GLX) else() find_package(X11) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${X11_INCLUDE_DIR}) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${X11_LIBRARIES}) endif() # EGL context dependencies elseif(_component STREQUAL EglContext) find_package(EGL) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL) endif() # No additional dependencies for CGL context # No additional dependencies for WGL context # Audio library elseif(_component STREQUAL Audio) find_package(OpenAL) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${OPENAL_INCLUDE_DIR}) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${OPENAL_LIBRARY} Corrade::PluginManager) # No special setup for DebugTools library # GL library elseif(_component STREQUAL GL) if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES) # If the GLVND library (CMake 3.11+) was found, link to the # imported target. Otherwise (and also on all systems except # Linux) link to the classic libGL. Can't use # OpenGL_OpenGL_FOUND, because that one is set also if GLVND is # *not* found. WTF. Also can't just check for # OPENGL_opengl_LIBRARY because that's set even if # OpenGL_GL_PREFERENCE is explicitly set to LEGACY. find_package(OpenGL REQUIRED) if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES OpenGL::OpenGL) else() set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${OPENGL_gl_LIBRARY}) endif() elseif(MAGNUM_TARGET_GLES2) find_package(OpenGLES2 REQUIRED) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES OpenGLES2::OpenGLES2) elseif(MAGNUM_TARGET_GLES3) find_package(OpenGLES3 REQUIRED) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES OpenGLES3::OpenGLES3) endif() # MeshTools library elseif(_component STREQUAL MeshTools) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES CompressIndices.h) # OpenGLTester library elseif(_component STREQUAL OpenGLTester) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/GL) # Primitives library elseif(_component STREQUAL Primitives) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES Cube.h) # No special setup for SceneGraph library # No special setup for Shaders library # Text library elseif(_component STREQUAL Text) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Corrade::PluginManager) # TextureTools library elseif(_component STREQUAL TextureTools) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES Atlas.h) # Trade library elseif(_component STREQUAL Trade) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Corrade::PluginManager) # Vk library elseif(_component STREQUAL Vk) set(Vulkan_INCLUDE_DIR ${MAGNUM_INCLUDE_DIR}/MagnumExternal/Vulkan) find_package(Vulkan REQUIRED) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Vulkan::Vulkan) endif() # No special setup for AnyAudioImporter plugin # No special setup for AnyImageConverter plugin # No special setup for AnyImageImporter plugin # No special setup for AnySceneImporter plugin # No special setup for MagnumFont plugin # No special setup for MagnumFontConverter plugin # No special setup for ObjImporter plugin # No special setup for TgaImageConverter plugin # No special setup for TgaImporter plugin # No special setup for WavAudioImporter plugin # Find library/plugin includes if(_component MATCHES ${_MAGNUM_LIBRARY_COMPONENTS} OR _component MATCHES ${_MAGNUM_PLUGIN_COMPONENTS}) find_path(_MAGNUM_${_COMPONENT}_INCLUDE_DIR NAMES ${_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES} HINTS ${MAGNUM_INCLUDE_DIR}/${_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX}) mark_as_advanced(_MAGNUM_${_COMPONENT}_INCLUDE_DIR) endif() # Automatic import of static plugins. Skip in case the include dir was # not found -- that'll fail later with a proper message. if(_component MATCHES ${_MAGNUM_PLUGIN_COMPONENTS} AND _MAGNUM_${_COMPONENT}_INCLUDE_DIR) # Automatic import of static plugins file(READ ${_MAGNUM_${_COMPONENT}_INCLUDE_DIR}/configure.h _magnum${_component}Configure) string(FIND "${_magnum${_component}Configure}" "#define MAGNUM_${_COMPONENT}_BUILD_STATIC" _magnum${_component}_BUILD_STATIC) if(NOT _magnum${_component}_BUILD_STATIC EQUAL -1) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_SOURCES ${_MAGNUM_${_COMPONENT}_INCLUDE_DIR}/importStaticPlugin.cpp) endif() endif() # Link to core Magnum library, add inter-library dependencies. If there # are optional dependencies, defer adding them to later once we know if # they were found or not. if(_component MATCHES ${_MAGNUM_LIBRARY_COMPONENTS} OR _component MATCHES ${_MAGNUM_PLUGIN_COMPONENTS}) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Magnum::Magnum) set(_MAGNUM_${component}_OPTIONAL_DEPENDENCIES_TO_ADD ) foreach(_dependency ${_MAGNUM_${_component}_DEPENDENCIES}) if(NOT _MAGNUM_${_component}_${_dependency}_DEPENDENCY_IS_OPTIONAL) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Magnum::${_dependency}) else() list(APPEND _MAGNUM_${_component}_OPTIONAL_DEPENDENCIES_TO_ADD ${_dependency}) endif() endforeach() if(_MAGNUM_${_component}_OPTIONAL_DEPENDENCIES_TO_ADD) list(APPEND _MAGNUM_OPTIONAL_DEPENDENCIES_TO_ADD ${_component}) endif() endif() # Decide if the library was found if(((_component MATCHES ${_MAGNUM_LIBRARY_COMPONENTS} OR _component MATCHES ${_MAGNUM_PLUGIN_COMPONENTS}) AND _MAGNUM_${_COMPONENT}_INCLUDE_DIR AND (MAGNUM_${_COMPONENT}_LIBRARY_DEBUG OR MAGNUM_${_COMPONENT}_LIBRARY_RELEASE)) OR (_component MATCHES ${_MAGNUM_EXECUTABLE_COMPONENTS} AND MAGNUM_${_COMPONENT}_EXECUTABLE)) set(Magnum_${_component}_FOUND TRUE) else() set(Magnum_${_component}_FOUND FALSE) endif() endif() # Global aliases for Windowless*Application, *Application and *Context # components. If already set, unset them to avoid ambiguity. if(_component MATCHES "Windowless.+Application") if(NOT DEFINED _MAGNUM_WINDOWLESSAPPLICATION_ALIAS) set(_MAGNUM_WINDOWLESSAPPLICATION_ALIAS Magnum::${_component}) else() unset(_MAGNUM_WINDOWLESSAPPLICATION_ALIAS) endif() elseif(_component MATCHES ".+Application") if(NOT DEFINED _MAGNUM_APPLICATION_ALIAS) set(_MAGNUM_APPLICATION_ALIAS Magnum::${_component}) else() unset(_MAGNUM_APPLICATION_ALIAS) endif() elseif(_component MATCHES ".+Context") if(NOT DEFINED _MAGNUM_GLCONTEXT_ALIAS) set(_MAGNUM_GLCONTEXT_ALIAS Magnum::${_component}) else() unset(_MAGNUM_GLCONTEXT_ALIAS) endif() endif() endforeach() # Emscripten-specific files and flags if(CORRADE_TARGET_EMSCRIPTEN) find_file(MAGNUM_EMSCRIPTENAPPLICATION_JS EmscriptenApplication.js PATH_SUFFIXES share/magnum) find_file(MAGNUM_WINDOWLESSEMSCRIPTENAPPLICATION_JS WindowlessEmscriptenApplication.js PATH_SUFFIXES share/magnum) find_file(MAGNUM_WEBAPPLICATION_CSS WebApplication.css PATH_SUFFIXES share/magnum) mark_as_advanced( MAGNUM_EMSCRIPTENAPPLICATION_JS MAGNUM_WINDOWLESSEMSCRIPTENAPPLICATION_JS MAGNUM_WEBAPPLICATION_CSS) set(MAGNUM_EXTRAS_NEEDED MAGNUM_EMSCRIPTENAPPLICATION_JS MAGNUM_WINDOWLESSEMSCRIPTENAPPLICATION_JS MAGNUM_WEBAPPLICATION_CSS) # If we are on CMake 3.13 and up, `-s USE_WEBGL2=1` linker option is # propagated from FindOpenGLES3.cmake already. If not (and the GL library # is used), we need to modify the global CMAKE_EXE_LINKER_FLAGS. Do it here # instead of in FindOpenGLES3.cmake so it works also for CMake subprojects # (in which case find_package(OpenGLES3) is called in (and so # CMAKE_EXE_LINKER_FLAGS would be modified in) Magnum's root CMakeLists.txt # and thus can't affect the variable in the outer project). CMake supports # IN_LIST as an operator since 3.1 (Emscripten needs at least 3.7), but # it's behind a policy, so enable that one as well. cmake_policy(SET CMP0057 NEW) if(CMAKE_VERSION VERSION_LESS 3.13 AND GL IN_LIST Magnum_FIND_COMPONENTS AND NOT MAGNUM_TARGET_GLES2 AND NOT CMAKE_EXE_LINKER_FLAGS MATCHES "-s USE_WEBGL2=1") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_WEBGL2=1") endif() endif() # Complete the check with also all components include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Magnum REQUIRED_VARS MAGNUM_INCLUDE_DIR MAGNUM_LIBRARY ${MAGNUM_EXTRAS_NEEDED} HANDLE_COMPONENTS) # Components with optional dependencies -- add them once we know if they were # found or not. foreach(_component ${_MAGNUM_OPTIONAL_DEPENDENCIES_TO_ADD}) foreach(_dependency ${_MAGNUM_${_component}_OPTIONAL_DEPENDENCIES_TO_ADD}) if(Magnum_${_dependency}_FOUND) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Magnum::${_dependency}) endif() endforeach() endforeach() # Create Windowless*Application, *Application and *Context aliases # TODO: ugh why can't I make an alias of IMPORTED target? if(_MAGNUM_WINDOWLESSAPPLICATION_ALIAS AND NOT TARGET Magnum::WindowlessApplication) get_target_property(_MAGNUM_WINDOWLESSAPPLICATION_ALIASED_TARGET ${_MAGNUM_WINDOWLESSAPPLICATION_ALIAS} ALIASED_TARGET) if(_MAGNUM_WINDOWLESSAPPLICATION_ALIASED_TARGET) add_library(Magnum::WindowlessApplication ALIAS ${_MAGNUM_WINDOWLESSAPPLICATION_ALIASED_TARGET}) else() add_library(Magnum::WindowlessApplication UNKNOWN IMPORTED) foreach(property IMPORTED_CONFIGURATIONS INTERFACE_INCLUDE_DIRECTORIES INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_OPTIONS INTERFACE_LINK_LIBRARIES) get_target_property(_MAGNUM_WINDOWLESSAPPLICATION_${property} ${_MAGNUM_WINDOWLESSAPPLICATION_ALIAS} ${property}) if(_MAGNUM_WINDOWLESSAPPLICATION_${property}) set_target_properties(Magnum::WindowlessApplication PROPERTIES ${property} "${_MAGNUM_WINDOWLESSAPPLICATION_${property}}") endif() endforeach() get_target_property(_MAGNUM_WINDOWLESSAPPLICATION_IMPORTED_LOCATION_RELEASE ${_MAGNUM_WINDOWLESSAPPLICATION_ALIAS} IMPORTED_LOCATION_RELEASE) get_target_property(_MAGNUM_WINDOWLESSAPPLICATION_IMPORTED_LOCATION_DEBUG ${_MAGNUM_WINDOWLESSAPPLICATION_ALIAS} IMPORTED_LOCATION_DEBUG) if(_MAGNUM_WINDOWLESSAPPLICATION_IMPORTED_LOCATION_RELEASE) set_target_properties(Magnum::WindowlessApplication PROPERTIES IMPORTED_LOCATION_RELEASE ${_MAGNUM_WINDOWLESSAPPLICATION_IMPORTED_LOCATION_RELEASE}) endif() if(_MAGNUM_WINDOWLESSAPPLICATION_IMPORTED_LOCATION_DEBUG) set_target_properties(Magnum::WindowlessApplication PROPERTIES IMPORTED_LOCATION_DEBUG ${_MAGNUM_WINDOWLESSAPPLICATION_IMPORTED_LOCATION_DEBUG}) endif() endif() # Prevent creating the alias again unset(_MAGNUM_WINDOWLESSAPPLICATION_ALIAS) endif() if(_MAGNUM_APPLICATION_ALIAS AND NOT TARGET Magnum::Application) get_target_property(_MAGNUM_APPLICATION_ALIASED_TARGET ${_MAGNUM_APPLICATION_ALIAS} ALIASED_TARGET) if(_MAGNUM_APPLICATION_ALIASED_TARGET) add_library(Magnum::Application ALIAS ${_MAGNUM_APPLICATION_ALIASED_TARGET}) else() add_library(Magnum::Application UNKNOWN IMPORTED) foreach(property IMPORTED_CONFIGURATIONS INTERFACE_INCLUDE_DIRECTORIES INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_OPTIONS INTERFACE_LINK_LIBRARIES) get_target_property(_MAGNUM_APPLICATION_${property} ${_MAGNUM_APPLICATION_ALIAS} ${property}) if(_MAGNUM_APPLICATION_${property}) set_target_properties(Magnum::Application PROPERTIES ${property} "${_MAGNUM_APPLICATION_${property}}") endif() endforeach() get_target_property(_MAGNUM_APPLICATION_IMPORTED_LOCATION_RELEASE ${_MAGNUM_APPLICATION_ALIAS} IMPORTED_LOCATION_RELEASE) get_target_property(_MAGNUM_APPLICATION_IMPORTED_LOCATION_DEBUG ${_MAGNUM_APPLICATION_ALIAS} IMPORTED_LOCATION_DEBUG) if(_MAGNUM_APPLICATION_IMPORTED_LOCATION_RELEASE) set_target_properties(Magnum::Application PROPERTIES IMPORTED_LOCATION_RELEASE ${_MAGNUM_APPLICATION_IMPORTED_LOCATION_RELEASE}) endif() if(_MAGNUM_APPLICATION_IMPORTED_LOCATION_DEBUG) set_target_properties(Magnum::Application PROPERTIES IMPORTED_LOCATION_DEBUG ${_MAGNUM_APPLICATION_IMPORTED_LOCATION_DEBUG}) endif() endif() # Prevent creating the alias again unset(_MAGNUM_APPLICATION_ALIAS) endif() if(_MAGNUM_GLCONTEXT_ALIAS AND NOT TARGET Magnum::GLContext) get_target_property(_MAGNUM_GLCONTEXT_ALIASED_TARGET ${_MAGNUM_GLCONTEXT_ALIAS} ALIASED_TARGET) if(_MAGNUM_GLCONTEXT_ALIASED_TARGET) add_library(Magnum::GLContext ALIAS ${_MAGNUM_GLCONTEXT_ALIASED_TARGET}) else() add_library(Magnum::GLContext UNKNOWN IMPORTED) foreach(property IMPORTED_CONFIGURATIONS INTERFACE_INCLUDE_DIRECTORIES INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_OPTIONS INTERFACE_LINK_LIBRARIES) get_target_property(_MAGNUM_GLCONTEXT_${property} ${_MAGNUM_GLCONTEXT_ALIAS} ${property}) if(_MAGNUM_GLCONTEXT_${property}) set_target_properties(Magnum::GLContext PROPERTIES ${property} "${_MAGNUM_GLCONTEXT_${property}}") endif() endforeach() get_target_property(_MAGNUM_GLCONTEXT_IMPORTED_LOCATION_RELEASE ${_MAGNUM_GLCONTEXT_ALIAS} IMPORTED_LOCATION_RELEASE) get_target_property(_MAGNUM_GLCONTEXT_IMPORTED_LOCATION_DEBUG ${_MAGNUM_GLCONTEXT_ALIAS} IMPORTED_LOCATION_DEBUG) if(_MAGNUM_GLCONTEXT_IMPORTED_LOCATION_RELEASE) set_target_properties(Magnum::GLContext PROPERTIES IMPORTED_LOCATION_RELEASE ${_MAGNUM_GLCONTEXT_IMPORTED_LOCATION_RELEASE}) endif() if(_MAGNUM_GLCONTEXT_IMPORTED_LOCATION_DEBUG) set_target_properties(Magnum::GLContext PROPERTIES IMPORTED_LOCATION_DEBUG ${_MAGNUM_GLCONTEXT_IMPORTED_LOCATION_DEBUG}) endif() endif() # Prevent creating the alias again unset(_MAGNUM_GLCONTEXT_ALIAS) endif() # Installation and deploy dirs set(MAGNUM_DEPLOY_PREFIX "." CACHE STRING "Prefix where to put final application executables") set(MAGNUM_INCLUDE_INSTALL_PREFIX "." CACHE STRING "Prefix where to put platform-independent include and other files") include(${CORRADE_LIB_SUFFIX_MODULE}) set(MAGNUM_BINARY_INSTALL_DIR bin) set(MAGNUM_LIBRARY_INSTALL_DIR lib${LIB_SUFFIX}) set(MAGNUM_DATA_INSTALL_DIR ${MAGNUM_INCLUDE_INSTALL_PREFIX}/share/magnum) set(MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_BINARY_INSTALL_DIR}/magnum-d) set(MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_LIBRARY_INSTALL_DIR}/magnum-d) set(MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_BINARY_INSTALL_DIR}/magnum) set(MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_LIBRARY_INSTALL_DIR}/magnum) set(MAGNUM_PLUGINS_FONT_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/fonts) set(MAGNUM_PLUGINS_FONT_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}/fonts) set(MAGNUM_PLUGINS_FONT_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/fonts) set(MAGNUM_PLUGINS_FONT_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/fonts) set(MAGNUM_PLUGINS_FONTCONVERTER_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/fontconverters) set(MAGNUM_PLUGINS_FONTCONVERTER_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/fontconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/imageconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}/imageconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/imageconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/imageconverters) set(MAGNUM_PLUGINS_IMPORTER_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/importers) set(MAGNUM_PLUGINS_IMPORTER_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}/importers) set(MAGNUM_PLUGINS_IMPORTER_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/importers) set(MAGNUM_PLUGINS_IMPORTER_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/importers) set(MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/audioimporters) set(MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}/audioimporters) set(MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/audioimporters) set(MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/audioimporters) set(MAGNUM_INCLUDE_INSTALL_DIR ${MAGNUM_INCLUDE_INSTALL_PREFIX}/include/Magnum) set(MAGNUM_EXTERNAL_INCLUDE_INSTALL_DIR ${MAGNUM_INCLUDE_INSTALL_PREFIX}/include/MagnumExternal) set(MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR ${MAGNUM_INCLUDE_INSTALL_PREFIX}/include/MagnumPlugins) # Get base plugin directory from main library location. This is *not* PATH, # because CMake always converts the path to an absolute location internally, # making it impossible to specify relative paths there. Sorry in advance for # not having the dir selection button in CMake GUI. set(MAGNUM_PLUGINS_DEBUG_DIR ${_MAGNUM_PLUGINS_DIR_PREFIX}/magnum-d CACHE STRING "Base directory where to look for Magnum plugins for debug builds") set(MAGNUM_PLUGINS_RELEASE_DIR ${_MAGNUM_PLUGINS_DIR_PREFIX}/magnum CACHE STRING "Base directory where to look for Magnum plugins for release builds") set(MAGNUM_PLUGINS_DIR ${_MAGNUM_PLUGINS_DIR_PREFIX}/magnum${_MAGNUM_PLUGINS_DIR_SUFFIX} CACHE STRING "Base directory where to look for Magnum plugins") # Plugin directories set(MAGNUM_PLUGINS_FONT_DIR ${MAGNUM_PLUGINS_DIR}/fonts) set(MAGNUM_PLUGINS_FONT_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/fonts) set(MAGNUM_PLUGINS_FONT_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/fonts) set(MAGNUM_PLUGINS_FONTCONVERTER_DIR ${MAGNUM_PLUGINS_DIR}/fontconverters) set(MAGNUM_PLUGINS_FONTCONVERTER_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/fontconverters) set(MAGNUM_PLUGINS_FONTCONVERTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/fontconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_DIR ${MAGNUM_PLUGINS_DIR}/imageconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/imageconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/imageconverters) set(MAGNUM_PLUGINS_IMPORTER_DIR ${MAGNUM_PLUGINS_DIR}/importers) set(MAGNUM_PLUGINS_IMPORTER_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/importers) set(MAGNUM_PLUGINS_IMPORTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/importers) set(MAGNUM_PLUGINS_AUDIOIMPORTER_DIR ${MAGNUM_PLUGINS_DIR}/audioimporters) set(MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/audioimporters) set(MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/audioimporters) ================================================ FILE: src/cmake/modules/FindMagnumIntegration.cmake ================================================ #.rst: # Find Magnum integration library # ------------------------------- # # Finds the Magnum integration library. Basic usage:: # # find_package(MagnumIntegration REQUIRED) # # This command tries to find Magnum integration library and then defines the # following: # # MagnumIntegration_FOUND - Whether the library was found # # This command alone is useless without specifying the components: # # Bullet - Bullet Physics integration library # Dart - Dart Physics integration library # Eigen - Eigen integration library # Glm - GLM integration library # ImGui - ImGui integration library # Ovr - Oculus SDK integration library # # Example usage with specifying additional components is: # # find_package(MagnumIntegration REQUIRED Bullet) # # For each component is then defined: # # MagnumIntegration_*_FOUND - Whether the component was found # MagnumIntegration::* - Component imported target # # The package is found if either debug or release version of each requested # library is found. If both debug and release libraries are found, proper # version is chosen based on actual build configuration of the project (i.e. # Debug build is linked to debug libraries, Release build to release # libraries). # # Additionally these variables are defined for internal usage: # # MAGNUMINTEGRATION_INCLUDE_DIR - Magnum integration include dir (w/o # dependencies) # MAGNUMINTEGRATION_*_LIBRARY_DEBUG - Debug version of given library, if found # MAGNUMINTEGRATION_*_LIBRARY_RELEASE - Release version of given library, if # found # # # This file is part of Magnum. # # Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, # 2020 Vladimír Vondruš # Copyright © 2018 Konstantinos Chatzilygeroudis # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # Magnum library dependencies set(_MAGNUMINTEGRATION_DEPENDENCIES ) foreach(_component ${MagnumIntegration_FIND_COMPONENTS}) if(_component STREQUAL Bullet) set(_MAGNUMINTEGRATION_${_component}_MAGNUM_DEPENDENCIES SceneGraph Shaders GL) elseif(_component STREQUAL Dart) set(_MAGNUMINTEGRATION_${_component}_MAGNUM_DEPENDENCIES SceneGraph Primitives MeshTools GL) elseif(_component STREQUAL ImGui) set(_MAGNUMINTEGRATION_${_component}_MAGNUM_DEPENDENCIES GL Shaders) endif() list(APPEND _MAGNUMINTEGRATION_DEPENDENCIES ${_MAGNUMINTEGRATION_${_component}_MAGNUM_DEPENDENCIES}) list(APPEND _MAGNUMINTEGRATION_OPTIONAL_DEPENDENCIES ${_MAGNUMINTEGRATION_${_component}_MAGNUM_OPTIONAL_DEPENDENCIES}) endforeach() find_package(Magnum REQUIRED ${_MAGNUMINTEGRATION_DEPENDENCIES}) if(_MAGNUMINTEGRATION_OPTIONAL_DEPENDENCIES) find_package(Magnum OPTIONAL_COMPONENTS ${_MAGNUMINTEGRATION_OPTIONAL_DEPENDENCIES}) endif() # Global integration include dir find_path(MAGNUMINTEGRATION_INCLUDE_DIR Magnum HINTS ${MAGNUM_INCLUDE_DIR}) mark_as_advanced(MAGNUMINTEGRATION_INCLUDE_DIR) # Component distinction (listing them explicitly to avoid mistakes with finding # components from other repositories) set(_MAGNUMINTEGRATION_LIBRARY_COMPONENT_LIST Bullet Dart Eigen ImGui Glm Ovr) set(_MAGNUMINTEGRATION_HEADER_ONLY_COMPONENT_LIST Eigen) # Inter-component dependencies (none yet) # set(_MAGNUMINTEGRATION_Component_DEPENDENCIES Dependency) # Ensure that all inter-component dependencies are specified as well set(_MAGNUMINTEGRATION_ADDITIONAL_COMPONENTS ) foreach(_component ${MagnumIntegration_FIND_COMPONENTS}) # Mark the dependencies as required if the component is also required if(MagnumIntegration_FIND_REQUIRED_${_component}) foreach(_dependency ${_MAGNUMINTEGRATION_${_component}_DEPENDENCIES}) set(MagnumIntegration_FIND_REQUIRED_${_dependency} TRUE) endforeach() endif() list(APPEND _MAGNUMINTEGRATION_ADDITIONAL_COMPONENTS ${_MAGNUMINTEGRATION_${_component}_DEPENDENCIES}) endforeach() # Join the lists, remove duplicate components if(_MAGNUMINTEGRATION_ADDITIONAL_COMPONENTS) list(INSERT MagnumIntegration_FIND_COMPONENTS 0 ${_MAGNUMINTEGRATION_ADDITIONAL_COMPONENTS}) endif() if(MagnumIntegration_FIND_COMPONENTS) list(REMOVE_DUPLICATES MagnumIntegration_FIND_COMPONENTS) endif() # Convert components lists to regular expressions so I can use if(MATCHES). # TODO: Drop this once CMake 3.3 and if(IN_LIST) can be used foreach(_WHAT LIBRARY HEADER_ONLY) string(REPLACE ";" "|" _MAGNUMINTEGRATION_${_WHAT}_COMPONENTS "${_MAGNUMINTEGRATION_${_WHAT}_COMPONENT_LIST}") set(_MAGNUMINTEGRATION_${_WHAT}_COMPONENTS "^(${_MAGNUMINTEGRATION_${_WHAT}_COMPONENTS})$") endforeach() # Find all components foreach(_component ${MagnumIntegration_FIND_COMPONENTS}) string(TOUPPER ${_component} _COMPONENT) # Create imported target in case the library is found. If the project is # added as subproject to CMake, the target already exists and all the # required setup is already done from the build tree. if(TARGET MagnumIntegration::${_component}) set(MagnumIntegration_${_component}_FOUND TRUE) else() # Library components if(_component MATCHES ${_MAGNUMINTEGRATION_LIBRARY_COMPONENTS} AND NOT _component MATCHES ${_MAGNUMINTEGRATION_HEADER_ONLY_COMPONENTS}) add_library(MagnumIntegration::${_component} UNKNOWN IMPORTED) # Try to find both debug and release version find_library(MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_DEBUG Magnum${_component}Integration-d) find_library(MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_RELEASE Magnum${_component}Integration) mark_as_advanced(MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_DEBUG MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_RELEASE) if(MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_RELEASE) set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_property(TARGET MagnumIntegration::${_component} PROPERTY IMPORTED_LOCATION_RELEASE ${MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_RELEASE}) endif() if(MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_DEBUG) set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) set_property(TARGET MagnumIntegration::${_component} PROPERTY IMPORTED_LOCATION_DEBUG ${MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_DEBUG}) endif() endif() # Header-only library components if(_component MATCHES ${_MAGNUMINTEGRATION_HEADER_ONLY_COMPONENTS}) add_library(MagnumIntegration::${_component} INTERFACE IMPORTED) endif() # Bullet integration library if(_component STREQUAL Bullet) # On Emscripten, Bullet could be taken from ports. If that's the # case, propagate proper compiler flag. if(CORRADE_TARGET_EMSCRIPTEN) find_file(_MAGNUMINTEGRATION_${_COMPONENT}_CONFIGURE_FILE configure.h HINTS ${MAGNUMINTEGRATION_INCLUDE_DIR}/Magnum/${_component}Integration) file(READ ${_MAGNUMINTEGRATION_${_COMPONENT}_CONFIGURE_FILE} _magnum${_component}IntegrationConfigure) string(FIND "${_magnum${_component}IntegrationConfigure}" "#define MAGNUM_USE_EMSCRIPTEN_PORTS_BULLET" _magnum${_component}Integration_USE_EMSCRIPTEN_PORTS_BULLET) if(NOT _magnum${_component}Integration_USE_EMSCRIPTEN_PORTS_BULLET EQUAL -1) set(MAGNUM_USE_EMSCRIPTEN_PORTS_BULLET 1) endif() endif() if(MAGNUM_USE_EMSCRIPTEN_PORTS_BULLET) if(CMAKE_VERSION VERSION_LESS 3.13) message(FATAL_ERROR "BulletIntegration was compiled against emscripten-ports version but linking to it requires CMake 3.13 at least") endif() set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "SHELL:-s USE_BULLET=1") set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_LINK_OPTIONS "SHELL:-s USE_BULLET=1") else() find_package(Bullet) set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${BULLET_INCLUDE_DIRS}) # Need to handle special cases where both debug and release # libraries are available (in form of debug;A;optimized;B in # BULLET_LIBRARIES), thus appending them one by one foreach(lib BULLET_DYNAMICS_LIBRARY BULLET_COLLISION_LIBRARY BULLET_MATH_LIBRARY BULLET_SOFTBODY_LIBRARY) if(${lib}_DEBUG) set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "$<$>:${${lib}}>;$<$:${${lib}_DEBUG}>") else() set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${${lib}}) endif() endforeach() endif() set(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES MotionState.h) # Eigen integration library elseif(_component STREQUAL Eigen) find_package(Eigen3) # We could drop this once we can use at least 3.3.1 (Ubuntu 16.04 # has only 3.3 beta, which doesn't have this target yet), however # for Travis and AppVeyor we're using FindEigen3.cmake from the # downloaded sources (because the Eigen3Config.cmake, which # produces the actual targets, is not there -- only # Eigen3Config.cmake.in). See the YML files for an extended rant. # Also, FindEigen3 only defines EIGEN3_INCLUDE_DIR, not even # EIGEN3_INCLUDE_DIRS, so be extra careful. # http://eigen.tuxfamily.org/index.php?title=ChangeLog#Eigen_3.3.1 set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${EIGEN3_INCLUDE_DIR}) set(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES Integration.h) # ImGui integration library elseif(_component STREQUAL ImGui) find_package(ImGui) set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ImGui::ImGui) set(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES Integration.h) # GLM integration library elseif(_component STREQUAL Glm) find_package(GLM) set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES GLM::GLM) set(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES Integration.h) # Dart integration library elseif(_component STREQUAL Dart) find_package(DART 6.0.0 CONFIG REQUIRED) set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES dart) set(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES ConvertShapeNode.h) # Oculus SDK integration library elseif(_component STREQUAL Ovr) find_package(OVR) set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES OVR::OVR) set(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES OvrIntegration.h) endif() # Find library includes if(_component MATCHES ${_MAGNUMINTEGRATION_LIBRARY_COMPONENTS}) find_path(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_DIR NAMES ${_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES} HINTS ${MAGNUMINTEGRATION_INCLUDE_DIR}/Magnum/${_component}Integration) mark_as_advanced(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_DIR) endif() if(_component MATCHES ${_MAGNUMINTEGRATION_LIBRARY_COMPONENTS}) # Link to core Magnum library, add other Magnum required and # optional dependencies set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Magnum::Magnum) foreach(_dependency ${_MAGNUMINTEGRATION_${_component}_MAGNUM_DEPENDENCIES}) set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Magnum::${_dependency}) endforeach() foreach(_dependency ${_MAGNUMINTEGRATION_${_component}_MAGNUM_OPTIONAL_DEPENDENCIES}) if(Magnum_${_dependency}_FOUND) set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Magnum::${_dependency}) endif() endforeach() # Add inter-project dependencies foreach(_dependency ${_MAGNUMINTEGRATION_${_component}_DEPENDENCIES}) set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES MagnumIntegration::${_dependency}) endforeach() endif() # Decide if the library was found if(_component MATCHES ${_MAGNUMINTEGRATION_LIBRARY_COMPONENTS} AND _MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_DIR AND (_component MATCHES ${_MAGNUMINTEGRATION_HEADER_ONLY_COMPONENTS} OR MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_DEBUG OR MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_RELEASE)) set(MagnumIntegration_${_component}_FOUND TRUE) else() set(MagnumIntegration_${_component}_FOUND FALSE) endif() endif() endforeach() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(MagnumIntegration REQUIRED_VARS MAGNUMINTEGRATION_INCLUDE_DIR HANDLE_COMPONENTS) ================================================ FILE: src/cmake/modules/FindSDL2.cmake ================================================ #.rst: # Find SDL2 # --------- # # Finds the SDL2 library. This module defines: # # SDL2_FOUND - True if SDL2 library is found # SDL2::SDL2 - SDL2 imported target # # Additionally these variables are defined for internal usage: # # SDL2_LIBRARY_DEBUG - SDL2 debug library, if found # SDL2_LIBRARY_RELEASE - SDL2 release library, if found # SDL2_DLL_DEBUG - SDL2 debug DLL on Windows, if found # SDL2_DLL_RELEASE - SDL2 release DLL on Windows, if found # SDL2_INCLUDE_DIR - Root include dir # # # This file is part of Magnum. # # Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 # Vladimír Vondruš # Copyright © 2018 Jonathan Hale # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # In Emscripten SDL is linked automatically, thus no need to find the library. # Also the includes are in SDL subdirectory, not SDL2. if(CORRADE_TARGET_EMSCRIPTEN) set(_SDL2_PATH_SUFFIXES SDL) else() set(_SDL2_PATH_SUFFIXES SDL2) if(WIN32) # Precompiled libraries for MSVC are in x86/x64 subdirectories if(MSVC) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_SDL2_LIBRARY_PATH_SUFFIX lib/x64) elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) set(_SDL2_LIBRARY_PATH_SUFFIX lib/x86) endif() # Both includes and libraries for MinGW are in some directory deep # inside. There's also a CMake config file but it has HARDCODED path # to /opt/local/i686-w64-mingw32, which doesn't make ANY SENSE, # especially on Windows. elseif(MINGW) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_SDL2_LIBRARY_PATH_SUFFIX x86_64-w64-mingw32/lib) set(_SDL2_RUNTIME_PATH_SUFFIX x86_64-w64-mingw32/bin) list(APPEND _SDL2_PATH_SUFFIXES x86_64-w64-mingw32/include/SDL2) elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) set(_SDL2_LIBRARY_PATH_SUFFIX i686-w64-mingw32/lib) set(_SDL2_RUNTIME_PATH_SUFFIX i686-w64-mingw32/lib) list(APPEND _SDL2_PATH_SUFFIXES i686-w64-mingw32/include/SDL2) endif() else() message(FATAL_ERROR "Unsupported compiler") endif() endif() find_library(SDL2_LIBRARY_RELEASE # Compiling SDL2 from scratch on macOS creates dead libSDL2.so symlink # which CMake somehow prefers before the SDL2-2.0.dylib file. Making # the dylib first so it is preferred. Not sure how this maps to debug # config though :/ NAMES SDL2-2.0 SDL2 PATH_SUFFIXES ${_SDL2_LIBRARY_PATH_SUFFIX}) find_library(SDL2_LIBRARY_DEBUG NAMES SDL2d PATH_SUFFIXES ${_SDL2_LIBRARY_PATH_SUFFIX}) # FPHSA needs one of the _DEBUG/_RELEASE variables to check that the # library was found -- using SDL_LIBRARY, which will get populated by # select_library_configurations() below. set(SDL2_LIBRARY_NEEDED SDL2_LIBRARY) endif() include(SelectLibraryConfigurations) select_library_configurations(SDL2) # Include dir find_path(SDL2_INCLUDE_DIR # We must search file which is present only in SDL2 and not in SDL1. # Apparently when both SDL.h and SDL_scancode.h are specified, CMake is # happy enough that it found SDL.h and doesn't bother about the other. # # On macOS, where the includes are not in SDL2/SDL.h form (which would # solve this issue), but rather SDL2.framework/Headers/SDL.h, CMake might # find SDL.framework/Headers/SDL.h if SDL1 is installed, which is wrong. NAMES SDL_scancode.h PATH_SUFFIXES ${_SDL2_PATH_SUFFIXES}) # DLL on Windows if(CORRADE_TARGET_WINDOWS) find_file(SDL2_DLL_RELEASE NAMES SDL2.dll PATH_SUFFIXES ${_SDL2_RUNTIME_PATH_SUFFIX} ${_SDL2_LIBRARY_PATH_SUFFIX}) find_file(SDL2_DLL_DEBUG NAMES SDL2d.dll # not sure? PATH_SUFFIXES ${_SDL2_RUNTIME_PATH_SUFFIX} ${_SDL2_LIBRARY_PATH_SUFFIX}) endif() # iOS dependencies if(CORRADE_TARGET_IOS) set(_SDL2_FRAMEWORKS AudioToolbox AVFoundation CoreGraphics CoreMotion Foundation GameController QuartzCore UIKit) set(_SDL2_FRAMEWORK_LIBRARIES ) foreach(framework ${_SDL2_FRAMEWORKS}) find_library(_SDL2_${framework}_LIBRARY ${framework}) mark_as_advanced(_SDL2_${framework}_LIBRARY) list(APPEND _SDL2_FRAMEWORK_LIBRARIES ${_SDL2_${framework}_LIBRARY}) list(APPEND _SDL2_FRAMEWORK_LIBRARY_NAMES _SDL2_${framework}_LIBRARY) endforeach() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args("SDL2" DEFAULT_MSG ${SDL2_LIBRARY_NEEDED} ${_SDL2_FRAMEWORK_LIBRARY_NAMES} SDL2_INCLUDE_DIR) if(NOT TARGET SDL2::SDL2) if(SDL2_LIBRARY_NEEDED) add_library(SDL2::SDL2 UNKNOWN IMPORTED) # Work around BUGGY framework support on macOS # https://cmake.org/Bug/view.php?id=14105 if(CORRADE_TARGET_APPLE AND SDL2_LIBRARY_RELEASE MATCHES "\\.framework$") set_property(TARGET SDL2::SDL2 APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_property(TARGET SDL2::SDL2 PROPERTY IMPORTED_LOCATION_RELEASE ${SDL2_LIBRARY_RELEASE}/SDL2) else() if(SDL2_LIBRARY_RELEASE) set_property(TARGET SDL2::SDL2 APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_property(TARGET SDL2::SDL2 PROPERTY IMPORTED_LOCATION_RELEASE ${SDL2_LIBRARY_RELEASE}) endif() if(SDL2_LIBRARY_DEBUG) set_property(TARGET SDL2::SDL2 APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) set_property(TARGET SDL2::SDL2 PROPERTY IMPORTED_LOCATION_DEBUG ${SDL2_LIBRARY_DEBUG}) endif() endif() # Link additional `dl` and `pthread` libraries required by a static # build of SDL on Unixy platforms (except Apple, where it is most # probably some frameworks instead) if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE AND SDL2_LIBRARY MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$") find_package(Threads) set_property(TARGET SDL2::SDL2 APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS}) endif() # Link frameworks on iOS if(CORRADE_TARGET_IOS) set_property(TARGET SDL2::SDL2 APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${_SDL2_FRAMEWORK_LIBRARIES}) endif() else() add_library(SDL2::SDL2 INTERFACE IMPORTED) endif() set_property(TARGET SDL2::SDL2 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}) endif() mark_as_advanced(SDL2_INCLUDE_DIR) ================================================ FILE: src/cmake/util.cmake ================================================ set(CURRENT_DIR "${CMAKE_CURRENT_LIST_DIR}") macro(set_compiler_flags) if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Za") message(STATUS "Disable Microsoft language extensions: ${CMAKE_CXX_FLAGS}") endif() if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") message(STATUS "Added parallel build arguments to CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") endif() if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Ot") message(STATUS "Added optimization flags CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") endif() if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") else() add_compile_options(-Wall -Wextra -Wdelete-non-virtual-dtor) endif() add_definitions(-D_SCL_SECURE_NO_WARNINGS) # VS annoying warnings add_definitions(-D_CRT_SECURE_NO_WARNINGS) # VS annoying warnings add_definitions(-D_USE_MATH_DEFINES) # to enable stuff like M_PI endmacro() macro(find_modules) find_package(OpenCV REQUIRED) include_directories(SYSTEM ${OpenCV_INCLUDE_DIRS}) endmacro() macro(common_settings) set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_compiler_flags() find_modules() endmacro() macro(collect_sources_default name) file(GLOB SOURCES src/*.c src/*.cpp) file(GLOB HEADERS include/${name}/*.h include/${name}/*.hpp) message(STATUS "${name} sources ${SOURCES}") message(STATUS "${name} headers ${HEADERS}") endmacro() macro(set_default_properties target folder_name) set_target_properties(${target} PROPERTIES FOLDER ${folder_name}) endmacro() macro(add_library_default name) collect_sources_default(${name}) add_library(${name} STATIC ${SOURCES} ${HEADERS}) include_directories(include) set_default_properties(${name} "libs") target_include_directories(${name} PUBLIC include) endmacro() macro(add_app_default name src) message(STATUS "APP ${name} sources ${src}") add_executable(${name} ${src}) set_default_properties(${name} "apps") endmacro() macro(add_test_default name) collect_sources_default(${name}) add_executable(${name} ${SOURCES} ${HEADERS}) set_default_properties(${name} "tests") endmacro() ================================================ FILE: src/examples/CMakeLists.txt ================================================ if (BUILD_GUI_APPS) add_subdirectory(textured_triangle) add_subdirectory(picking_objects) add_subdirectory(arcball) add_subdirectory(bullet_example) endif() ================================================ FILE: src/examples/arcball/ArcBall.cpp ================================================ /* This file is part of Magnum. Original authors — credit is appreciated but not required: 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — Vladimír Vondruš 2020 — Nghia Truong This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "ArcBall.h" #include namespace Magnum { namespace Examples { namespace { /* Project a point in NDC onto the arcball sphere */ Quaternion ndcToArcBall(const Vector2& p) { const Float dist = Math::dot(p, p); /* Point is on sphere */ if(dist <= 1.0f) return {{p.x(), p.y(), Math::sqrt(1.0f - dist)}, 0.0f}; /* Point is outside sphere */ else { const Vector2 proj = p.normalized(); return {{proj.x(), proj.y(), 0.0f}, 0.0f}; } } } ArcBall::ArcBall(const Vector3& eye, const Vector3& viewCenter, const Vector3& upDir, Deg fov, const Vector2i& windowSize): _fov{fov}, _windowSize{windowSize} { setViewParameters(eye, viewCenter, upDir); } void ArcBall::setViewParameters(const Vector3& eye, const Vector3& viewCenter, const Vector3& upDir) { const Vector3 dir = viewCenter - eye; Vector3 zAxis = dir.normalized(); Vector3 xAxis = (Math::cross(zAxis, upDir.normalized())).normalized(); Vector3 yAxis = (Math::cross(xAxis, zAxis)).normalized(); xAxis = (Math::cross(zAxis, yAxis)).normalized(); _targetPosition = -viewCenter; _targetZooming = -dir.length(); _targetQRotation = Quaternion::fromMatrix( Matrix3x3{xAxis, yAxis, -zAxis}.transposed()).normalized(); _positionT0 = _currentPosition = _targetPosition; _zoomingT0 = _currentZooming = _targetZooming; _qRotationT0 = _currentQRotation = _targetQRotation; updateInternalTransformations(); } void ArcBall::reset() { _targetPosition = _positionT0; _targetZooming = _zoomingT0; _targetQRotation = _qRotationT0; } void ArcBall::setLagging(const Float lagging) { CORRADE_INTERNAL_ASSERT(lagging >= 0.0f && lagging < 1.0f); _lagging = lagging; } void ArcBall::initTransformation(const Vector2i& mousePos) { _prevMousePosNDC = screenCoordToNDC(mousePos); } void ArcBall::rotate(const Vector2i& mousePos) { const Vector2 mousePosNDC = screenCoordToNDC(mousePos); const Quaternion currentQRotation = ndcToArcBall(mousePosNDC); const Quaternion prevQRotation = ndcToArcBall(_prevMousePosNDC); _prevMousePosNDC = mousePosNDC; _targetQRotation = (currentQRotation*prevQRotation*_targetQRotation).normalized(); } void ArcBall::translate(const Vector2i& mousePos) { const Vector2 mousePosNDC = screenCoordToNDC(mousePos); const Vector2 translationNDC = mousePosNDC - _prevMousePosNDC; _prevMousePosNDC = mousePosNDC; translateDelta(translationNDC); } void ArcBall::translateDelta(const Vector2& translationNDC) { /* Half size of the screen viewport at the view center and perpendicular with the viewDir */ const Float hh = Math::abs(_targetZooming)*Math::tan(_fov*0.5f); const Float hw = hh*Vector2{_windowSize}.aspectRatio(); _targetPosition += _inverseView.transformVector( {translationNDC.x()*hw, translationNDC.y()*hh, 0.0f}); } void ArcBall::zoom(const Float delta) { _targetZooming += delta; } bool ArcBall::updateTransformation() { const Vector3 diffViewCenter = _targetPosition - _currentPosition; const Quaternion diffRotation = _targetQRotation - _currentQRotation; const Float diffZooming = _targetZooming - _currentZooming; const Float dViewCenter = Math::dot(diffViewCenter, diffViewCenter); const Float dRotation = Math::dot(diffRotation, diffRotation); const Float dZooming = diffZooming * diffZooming; /* Nothing change */ if(dViewCenter < 1.0e-10f && dRotation < 1.0e-10f && dZooming < 1.0e-10f) { return false; } /* Nearly done: just jump directly to the target */ if(dViewCenter < 1.0e-6f && dRotation < 1.0e-6f && dZooming < 1.0e-6f) { _currentPosition = _targetPosition; _currentQRotation = _targetQRotation; _currentZooming = _targetZooming; /* Interpolate between the current transformation and the target transformation */ } else { const Float t = 1 - _lagging; _currentPosition = Math::lerp(_currentPosition, _targetPosition, t); _currentZooming = Math::lerp(_currentZooming, _targetZooming, t); _currentQRotation = Math::slerpShortestPath( _currentQRotation, _targetQRotation, t); } updateInternalTransformations(); return true; } void ArcBall::updateInternalTransformations() { _view = DualQuaternion::translation(Vector3::zAxis(_currentZooming))* DualQuaternion{_currentQRotation}* DualQuaternion::translation(_currentPosition); _inverseView = _view.inverted(); } Vector2 ArcBall::screenCoordToNDC(const Vector2i& mousePos) const { return {mousePos.x()*2.0f/_windowSize.x() - 1.0f, 1.0f - 2.0f*mousePos.y()/ _windowSize.y()}; } }} ================================================ FILE: src/examples/arcball/ArcBall.h ================================================ #ifndef Magnum_Examples_ArcBall_h #define Magnum_Examples_ArcBall_h /* This file is part of Magnum. Original authors — credit is appreciated but not required: 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — Vladimír Vondruš 2020 — Nghia Truong This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include namespace Magnum { namespace Examples { /* Implementation of Ken Shoemake's arcball camera with smooth navigation feature: https://www.talisman.org/~erlkonig/misc/shoemake92-arcball.pdf */ class ArcBall { public: ArcBall(const Vector3& cameraPosition, const Vector3& viewCenter, const Vector3& upDir, Deg fov, const Vector2i& windowSize); /* Set the camera view parameters: eye position, view center, up direction */ void setViewParameters(const Vector3& eye, const Vector3& viewCenter, const Vector3& upDir); /* Reset the camera to its initial position, view center, and up dir */ void reset(); /* Update screen size after the window has been resized */ void reshape(const Vector2i& windowSize) { _windowSize = windowSize; } /* Update any unfinished transformation due to lagging, return true if the camera matrices have changed */ bool updateTransformation(); /* Get/set the amount of lagging such that the camera will (slowly) smoothly navigate. Lagging must be in [0, 1) */ Float lagging() const { return _lagging; } void setLagging(Float lagging); /* Initialize the first (screen) mouse position for camera transformation. This should be called in mouse pressed event. */ void initTransformation(const Vector2i& mousePos); /* Rotate the camera from the previous (screen) mouse position to the current (screen) position */ void rotate(const Vector2i& mousePos); /* Translate the camera from the previous (screen) mouse position to the current (screen) mouse position */ void translate(const Vector2i& mousePos); /* Translate the camera by the delta amount of (NDC) mouse position. Note that NDC position must be in [-1, -1] to [1, 1]. */ void translateDelta(const Vector2& translationNDC); /* Zoom the camera (positive delta = zoom in, negative = zoom out) */ void zoom(Float delta); /* Field of view */ Deg fov() const { return _fov; } /* Get the camera's view transformation as a qual quaternion */ const DualQuaternion& view() const { return _view; } /* Get the camera's view transformation as a matrix */ Matrix4 viewMatrix() const { return _view.toMatrix(); } /* Get the camera's inverse view matrix (which also produces transformation of the camera) */ Matrix4 inverseViewMatrix() const { return _inverseView.toMatrix(); } /* Get the camera's transformation as a dual quaternion */ const DualQuaternion& transformation() const { return _inverseView; } /* Get the camera's transformation matrix */ Matrix4 transformationMatrix() const { return _inverseView.toMatrix(); } /* Return the distance from the camera position to the center view */ Float viewDistance() const { return Math::abs(_targetZooming); } protected: /* Update the camera transformations */ void updateInternalTransformations(); /* Transform from screen coordinate to NDC - normalized device coordinate. The top-left of the screen corresponds to [-1, 1] NDC, and the bottom right is [1, -1] NDC. */ Vector2 screenCoordToNDC(const Vector2i& mousePos) const; Deg _fov; Vector2i _windowSize; Vector2 _prevMousePosNDC; Float _lagging{}; Vector3 _targetPosition, _currentPosition, _positionT0; Quaternion _targetQRotation, _currentQRotation, _qRotationT0; Float _targetZooming, _currentZooming, _zoomingT0; DualQuaternion _view, _inverseView; }; }} #endif ================================================ FILE: src/examples/arcball/ArcBallCamera.h ================================================ #ifndef Magnum_Examples_ArcBallCamera_h #define Magnum_Examples_ArcBallCamera_h /* This file is part of Magnum. Original authors — credit is appreciated but not required: 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — Vladimír Vondruš 2020 — Nghia Truong This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include "ArcBall.h" namespace Magnum { namespace Examples { /* Arcball camera implementation integrated into the SceneGraph */ class ArcBallCamera: public ArcBall { public: template ArcBallCamera( SceneGraph::Scene& scene, const Vector3& cameraPosition, const Vector3& viewCenter, const Vector3& upDir, Deg fov, const Vector2i& windowSize, const Vector2i& viewportSize): ArcBall{cameraPosition, viewCenter, upDir, fov, windowSize} { /* Create a camera object of a concrete type */ auto* cameraObject = new SceneGraph::Object{&scene}; (*(_camera = new SceneGraph::Camera3D{*cameraObject})) .setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend) .setProjectionMatrix(Matrix4::perspectiveProjection( fov, Vector2{windowSize}.aspectRatio(), 0.01f, 100.0f)) .setViewport(viewportSize); /* Save the abstract transformation interface and initialize the camera position through that */ (*(_cameraObject = cameraObject)) .rotate(transformation().rotation()) .translate(transformation().translation()); } /* Update screen and viewport size after the window has been resized */ void reshape(const Vector2i& windowSize, const Vector2i& viewportSize) { _windowSize = windowSize; _camera->setViewport(viewportSize); } /* Update the SceneGraph camera if arcball has been changed */ bool update() { /* call the internal update */ if(!updateTransformation()) return false; (*_cameraObject) .resetTransformation() .rotate(transformation().rotation()) .translate(transformation().translation()); return true; } /* Draw objects using the internal scenegraph camera */ void draw(SceneGraph::DrawableGroup3D& drawables) { _camera->draw(drawables); } /* Accessor to the raw camera object */ SceneGraph::Camera3D& camera() const { return *_camera; } private: SceneGraph::AbstractTranslationRotation3D* _cameraObject{}; SceneGraph::Camera3D* _camera{}; }; }} #endif ================================================ FILE: src/examples/arcball/ArcBallExample.cpp ================================================ /* This file is part of Magnum. Original authors — credit is appreciated but not required: 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — Vladimír Vondruš 2020 — Nghia Truong This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ArcBall.h" #include "ArcBallCamera.h" namespace Magnum { namespace Examples { using Object3D = SceneGraph::Object; using Scene3D = SceneGraph::Scene; using namespace Math::Literals; class ArcBallExample: public Platform::Application { public: explicit ArcBallExample(const Arguments& arguments); private: void drawEvent() override; void viewportEvent(ViewportEvent& event) override; void keyPressEvent(KeyEvent& event) override; void mousePressEvent(MouseEvent& event) override; void mouseReleaseEvent(MouseEvent& event) override; void mouseMoveEvent(MouseMoveEvent& event) override; void mouseScrollEvent(MouseScrollEvent& event) override; Scene3D _scene; SceneGraph::DrawableGroup3D _drawables; GL::Mesh _mesh{NoCreate}; Containers::Optional _arcballCamera; /* Stuff for visualizing the cube */ Shaders::MeshVisualizer3D _shader{NoCreate}; GL::Texture2D _colormap{NoCreate}; }; class VisualizationDrawable: public SceneGraph::Drawable3D { public: explicit VisualizationDrawable(Object3D& object, Shaders::MeshVisualizer3D& shader, GL::Mesh& mesh, SceneGraph::DrawableGroup3D& drawables): SceneGraph::Drawable3D{object, &drawables}, _shader(shader), _mesh(mesh) {} void draw(const Matrix4& transformation, SceneGraph::Camera3D& camera) { _shader .setTransformationMatrix(transformation) .setProjectionMatrix(camera.projectionMatrix()) .draw(_mesh); } private: Shaders::MeshVisualizer3D& _shader; GL::Mesh& _mesh; }; ArcBallExample::ArcBallExample(const Arguments& arguments) : Platform::Application{arguments, NoCreate} { /* Setup window */ { const Vector2 dpiScaling = this->dpiScaling({}); Configuration conf; conf.setTitle("Magnum ArcBall Camera Example") .setSize(conf.size(), dpiScaling) .setWindowFlags(Configuration::WindowFlag::Resizable); GLConfiguration glConf; glConf.setSampleCount(dpiScaling.max() < 2.0f ? 8 : 2); if(!tryCreate(conf, glConf)) { create(conf, glConf.setSampleCount(0)); } } GL::Renderer::enable(GL::Renderer::Feature::DepthTest); GL::Renderer::enable(GL::Renderer::Feature::FaceCulling); /* Setup a cube with vertex ID and wireframe visualized */ { const Trade::MeshData cube = Primitives::cubeSolid(); _mesh = MeshTools::compile(cube); const auto map = DebugTools::ColorMap::turbo(); const Vector2i size{Int(map.size()), 1}; _colormap = GL::Texture2D{}; _colormap .setMinificationFilter(SamplerFilter::Linear) .setMagnificationFilter(SamplerFilter::Linear) .setWrapping(SamplerWrapping::ClampToEdge) .setStorage(1, GL::TextureFormat::RGB8, size) .setSubImage(0, {}, ImageView2D{PixelFormat::RGB8Unorm, size, map}); _shader = Shaders::MeshVisualizer3D{ Shaders::MeshVisualizer3D::Flag::Wireframe| Shaders::MeshVisualizer3D::Flag::VertexId}; _shader .setViewportSize(Vector2{framebufferSize()}) .setColor(0xffffff_rgbf) .setWireframeColor(0xffffff_rgbf) .setWireframeWidth(2.0f) .setColorMapTransformation(0.0f, 1.0f/cube.vertexCount()) .bindColorMapTexture(_colormap); auto object = new Object3D{&_scene}; (*object) .rotateY(40.0_degf) .rotateX(-30.0_degf) ; new VisualizationDrawable{*object, _shader, _mesh, _drawables}; } /* Set up the camera */ { /* Setup the arcball after the camera objects */ const Vector3 eye = Vector3::zAxis(-10.0f); const Vector3 center{}; const Vector3 up = Vector3::yAxis(); _arcballCamera.emplace(_scene, eye, center, up, 45.0_degf, windowSize(), framebufferSize()); } /* Loop at 60 Hz max */ setSwapInterval(1); setMinimalLoopPeriod(16); } void ArcBallExample::drawEvent() { GL::defaultFramebuffer.clear( GL::FramebufferClear::Color|GL::FramebufferClear::Depth); /* Call arcball update in every frame. This will do nothing if the camera has not been changed. Otherwise, camera transformation will be propagated into the camera objects. */ bool camChanged = _arcballCamera->update(); _arcballCamera->draw(_drawables); swapBuffers(); if(camChanged) redraw(); } void ArcBallExample::viewportEvent(ViewportEvent& event) { GL::defaultFramebuffer.setViewport({{}, event.framebufferSize()}); _arcballCamera->reshape(event.windowSize(), event.framebufferSize()); _shader.setViewportSize(Vector2{framebufferSize()}); } void ArcBallExample::keyPressEvent(KeyEvent& event) { switch(event.key()) { case KeyEvent::Key::L: if(_arcballCamera->lagging() > 0.0f) { Debug{} << "Lagging disabled"; _arcballCamera->setLagging(0.0f); } else { Debug{} << "Lagging enabled"; _arcballCamera->setLagging(0.85f); } break; case KeyEvent::Key::R: _arcballCamera->reset(); break; default: return; } event.setAccepted(); redraw(); /* camera has changed, redraw! */ } void ArcBallExample::mousePressEvent(MouseEvent& event) { /* Enable mouse capture so the mouse can drag outside of the window */ /** @todo replace once https://github.com/mosra/magnum/pull/419 is in */ SDL_CaptureMouse(SDL_TRUE); _arcballCamera->initTransformation(event.position()); event.setAccepted(); redraw(); /* camera has changed, redraw! */ } void ArcBallExample::mouseReleaseEvent(MouseEvent&) { /* Disable mouse capture again */ /** @todo replace once https://github.com/mosra/magnum/pull/419 is in */ SDL_CaptureMouse(SDL_FALSE); } void ArcBallExample::mouseMoveEvent(MouseMoveEvent& event) { if(!event.buttons()) return; if(event.modifiers() & MouseMoveEvent::Modifier::Shift) _arcballCamera->translate(event.position()); else _arcballCamera->rotate(event.position()); event.setAccepted(); redraw(); /* camera has changed, redraw! */ } void ArcBallExample::mouseScrollEvent(MouseScrollEvent& event) { const Float delta = event.offset().y(); if(Math::abs(delta) < 1.0e-2f) return; _arcballCamera->zoom(delta); event.setAccepted(); redraw(); /* camera has changed, redraw! */ } }} MAGNUM_APPLICATION_MAIN(Magnum::Examples::ArcBallExample) ================================================ FILE: src/examples/arcball/CMakeLists.txt ================================================ # # This file is part of Magnum. # # Original authors — credit is appreciated but not required: # # 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — # Vladimír Vondruš # 2020 — Nghia Truong # # This is free and unencumbered software released into the public domain. # # Anyone is free to copy, modify, publish, use, compile, sell, or distribute # this software, either in source code form or as a compiled binary, for any # purpose, commercial or non-commercial, and by any means. # # In jurisdictions that recognize copyright laws, the author or authors of # this software dedicate any and all copyright interest in the software to # the public domain. We make this dedication for the benefit of the public # at large and to the detriment of our heirs and successors. We intend this # dedication to be an overt act of relinquishment in perpetuity of all # present and future rights to this software under copyright law. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # cmake_minimum_required(VERSION 3.4) project(MagnumArcBallExample CXX) # Add module path in case this is project root if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../../modules/" ${CMAKE_MODULE_PATH}) endif() find_package(Corrade REQUIRED Main) find_package(Magnum REQUIRED DebugTools GL MeshTools Primitives SceneGraph Shaders Sdl2Application) set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) add_executable(magnum-arcball WIN32 ArcBall.cpp ArcBallExample.cpp) target_link_libraries(magnum-arcball PRIVATE Corrade::Main Magnum::Application Magnum::DebugTools Magnum::GL Magnum::Magnum Magnum::MeshTools Magnum::Primitives Magnum::SceneGraph Magnum::Shaders) install(TARGETS magnum-arcball DESTINATION ${MAGNUM_BINARY_INSTALL_DIR}) # Make the executable a default target to build & run in Visual Studio set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT magnum-arcball) ================================================ FILE: src/examples/bullet_example/CMakeLists.txt ================================================ add_executable(bullet_example WIN32 bullet_example.cpp) target_link_libraries(bullet_example PRIVATE Corrade::Main Magnum::Application Magnum::GL Magnum::Magnum Magnum::MeshTools Magnum::Primitives Magnum::SceneGraph Magnum::Shaders Magnum::Trade MagnumIntegration::Bullet ) ================================================ FILE: src/examples/bullet_example/bullet_example.cpp ================================================ /* This file is part of Magnum. Original authors — credit is appreciated but not required: 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — Vladimír Vondruš 2013 — Jan Dupal 2019 — Max Schwarz This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Magnum { namespace Examples { using namespace Math::Literals; typedef SceneGraph::Object Object3D; typedef SceneGraph::Scene Scene3D; struct InstanceData { Matrix4 transformationMatrix; Matrix3x3 normalMatrix; Color3 color; }; class BulletDummy: public Platform::Application { public: explicit BulletDummy(const Arguments& arguments); private: void drawEvent() override; void keyPressEvent(KeyEvent& event) override; void mousePressEvent(MouseEvent& event) override; GL::Mesh _box{NoCreate}, _sphere{NoCreate}; GL::Buffer _boxInstanceBuffer{NoCreate}, _sphereInstanceBuffer{NoCreate}; Shaders::Phong _shader{NoCreate}; BulletIntegration::DebugDraw _debugDraw{NoCreate}; Containers::Array _boxInstanceData, _sphereInstanceData; btDbvtBroadphase _bBroadphase; btDefaultCollisionConfiguration _bCollisionConfig; btCollisionDispatcher _bDispatcher{&_bCollisionConfig}; btSequentialImpulseConstraintSolver _bSolver; /* The world has to live longer than the scene because RigidBody instances have to remove themselves from it on destruction */ btDiscreteDynamicsWorld _bWorld{&_bDispatcher, &_bBroadphase, &_bSolver, &_bCollisionConfig}; Scene3D _scene; SceneGraph::Camera3D* _camera; SceneGraph::DrawableGroup3D _drawables; Timeline _timeline; Object3D *_cameraRig, *_cameraObject; btBoxShape _bBoxShape{{0.5f, 0.5f, 0.5f}}; btSphereShape _bSphereShape{0.25f}; btBoxShape _bGroundShape{{4.0f, 0.5f, 4.0f}}; bool _drawCubes{true}, _drawDebug{true}, _shootBox{true}; }; class ColoredDrawable: public SceneGraph::Drawable3D { public: explicit ColoredDrawable(Object3D& object, Containers::Array& instanceData, const Color3& color, const Matrix4& primitiveTransformation, SceneGraph::DrawableGroup3D& drawables): SceneGraph::Drawable3D{object, &drawables}, _instanceData(instanceData), _color{color}, _primitiveTransformation{primitiveTransformation} {} private: void draw(const Matrix4& transformation, SceneGraph::Camera3D&) override { const Matrix4 t = transformation*_primitiveTransformation; arrayAppend(_instanceData, Containers::InPlaceInit, t, t.normalMatrix(), _color); } Containers::Array& _instanceData; Color3 _color; Matrix4 _primitiveTransformation; }; class RigidBody: public Object3D { public: RigidBody(Object3D* parent, Float mass, btCollisionShape* bShape, btDynamicsWorld& bWorld): Object3D{parent}, _bWorld(bWorld) { /* Calculate inertia so the object reacts as it should with rotation and everything */ btVector3 bInertia(0.0f, 0.0f, 0.0f); if(!Math::TypeTraits::equals(mass, 0.0f)) bShape->calculateLocalInertia(mass, bInertia); /* Bullet rigid body setup */ auto* motionState = new BulletIntegration::MotionState{*this}; _bRigidBody.emplace(btRigidBody::btRigidBodyConstructionInfo{ mass, &motionState->btMotionState(), bShape, bInertia}); _bRigidBody->forceActivationState(DISABLE_DEACTIVATION); bWorld.addRigidBody(_bRigidBody.get()); } ~RigidBody() { _bWorld.removeRigidBody(_bRigidBody.get()); } btRigidBody& rigidBody() { return *_bRigidBody; } /* needed after changing the pose from Magnum side */ void syncPose() { _bRigidBody->setWorldTransform(btTransform(transformationMatrix())); } private: btDynamicsWorld& _bWorld; Containers::Pointer _bRigidBody; }; BulletDummy::BulletDummy(const Arguments& arguments): Platform::Application(arguments, NoCreate) { /* Try 8x MSAA, fall back to zero samples if not possible. Enable only 2x MSAA if we have enough DPI. */ { const Vector2 dpiScaling = this->dpiScaling({}); Configuration conf; conf.setTitle("Magnum Bullet Integration Example") .setSize(conf.size(), dpiScaling); GLConfiguration glConf; glConf.setSampleCount(dpiScaling.max() < 2.0f ? 8 : 2); if(!tryCreate(conf, glConf)) create(conf, glConf.setSampleCount(0)); } /* Camera setup */ (*(_cameraRig = new Object3D{&_scene})) .translate(Vector3::yAxis(3.0f)) .rotateY(40.0_degf); (*(_cameraObject = new Object3D{_cameraRig})) .translate(Vector3::zAxis(20.0f)) .rotateX(-25.0_degf); (_camera = new SceneGraph::Camera3D(*_cameraObject)) ->setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend) .setProjectionMatrix(Matrix4::perspectiveProjection(35.0_degf, 1.0f, 0.001f, 100.0f)) .setViewport(GL::defaultFramebuffer.viewport().size()); /* Create an instanced shader */ _shader = Shaders::Phong{ Shaders::Phong::Flag::VertexColor| Shaders::Phong::Flag::InstancedTransformation}; _shader.setAmbientColor(0x111111_rgbf) .setSpecularColor(0x330000_rgbf) .setLightPosition({10.0f, 15.0f, 5.0f}); /* Box and sphere mesh, with an (initially empty) instance buffer */ _box = MeshTools::compile(Primitives::cubeSolid()); _sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); _boxInstanceBuffer = GL::Buffer{}; _sphereInstanceBuffer = GL::Buffer{}; _box.addVertexBufferInstanced(_boxInstanceBuffer, 1, 0, Shaders::Phong::TransformationMatrix{}, Shaders::Phong::NormalMatrix{}, Shaders::Phong::Color3{}); _sphere.addVertexBufferInstanced(_sphereInstanceBuffer, 1, 0, Shaders::Phong::TransformationMatrix{}, Shaders::Phong::NormalMatrix{}, Shaders::Phong::Color3{}); /* Setup the renderer so we can draw the debug lines on top */ GL::Renderer::enable(GL::Renderer::Feature::DepthTest); GL::Renderer::enable(GL::Renderer::Feature::FaceCulling); GL::Renderer::enable(GL::Renderer::Feature::PolygonOffsetFill); GL::Renderer::setPolygonOffset(2.0f, 0.5f); /* Bullet setup */ _debugDraw = BulletIntegration::DebugDraw{}; _debugDraw.setMode(BulletIntegration::DebugDraw::Mode::DrawWireframe); _bWorld.setGravity({0.0f, -10.0f, 0.0f}); _bWorld.setDebugDrawer(&_debugDraw); /* Create the ground */ auto* ground = new RigidBody{&_scene, 0.0f, &_bGroundShape, _bWorld}; new ColoredDrawable{*ground, _boxInstanceData, 0xffffff_rgbf, Matrix4::scaling({4.0f, 0.5f, 4.0f}), _drawables}; /* Create boxes with random colors */ Deg hue = 42.0_degf; for(Int i = 0; i != 5; ++i) { for(Int j = 0; j != 5; ++j) { for(Int k = 0; k != 5; ++k) { auto* o = new RigidBody{&_scene, 1.0f, &_bBoxShape, _bWorld}; o->translate({i - 2.0f, j + 4.0f, k - 2.0f}); o->syncPose(); new ColoredDrawable{*o, _boxInstanceData, Color3::fromHsv({hue += 137.5_degf, 0.75f, 0.9f}), Matrix4::scaling(Vector3{0.5f}), _drawables}; } } } /* Loop at 60 Hz max */ setSwapInterval(1); setMinimalLoopPeriod(16); _timeline.start(); } void BulletDummy::drawEvent() { GL::defaultFramebuffer.clear(GL::FramebufferClear::Color|GL::FramebufferClear::Depth); /* Housekeeping: remove any objects which are far away from the origin */ for(Object3D* obj = _scene.children().first(); obj; ) { Object3D* next = obj->nextSibling(); if(obj->transformation().translation().dot() > 100*100) delete obj; obj = next; } /* Step bullet simulation */ _bWorld.stepSimulation(_timeline.previousFrameDuration(), 5); if(_drawCubes) { /* Populate instance data with transformations and colors */ arrayResize(_boxInstanceData, 0); arrayResize(_sphereInstanceData, 0); _camera->draw(_drawables); _shader.setProjectionMatrix(_camera->projectionMatrix()); /* Upload instance data to the GPU (orphaning the previous buffer contents) and draw all cubes in one call, and all spheres (if any) in another call */ _boxInstanceBuffer.setData(_boxInstanceData, GL::BufferUsage::DynamicDraw); _box.setInstanceCount(_boxInstanceData.size()); _shader.draw(_box); _sphereInstanceBuffer.setData(_sphereInstanceData, GL::BufferUsage::DynamicDraw); _sphere.setInstanceCount(_sphereInstanceData.size()); _shader.draw(_sphere); } /* Debug draw. If drawing on top of cubes, avoid flickering by setting depth function to <= instead of just <. */ if(_drawDebug) { if(_drawCubes) GL::Renderer::setDepthFunction(GL::Renderer::DepthFunction::LessOrEqual); _debugDraw.setTransformationProjectionMatrix( _camera->projectionMatrix()*_camera->cameraMatrix()); _bWorld.debugDrawWorld(); if(_drawCubes) GL::Renderer::setDepthFunction(GL::Renderer::DepthFunction::Less); } swapBuffers(); _timeline.nextFrame(); redraw(); } void BulletDummy::keyPressEvent(KeyEvent& event) { /* Movement */ if(event.key() == KeyEvent::Key::Down) { _cameraObject->rotateX(5.0_degf); } else if(event.key() == KeyEvent::Key::Up) { _cameraObject->rotateX(-5.0_degf); } else if(event.key() == KeyEvent::Key::Left) { _cameraRig->rotateY(-5.0_degf); } else if(event.key() == KeyEvent::Key::Right) { _cameraRig->rotateY(5.0_degf); /* Toggling draw modes */ } else if(event.key() == KeyEvent::Key::D) { if(_drawCubes && _drawDebug) { _drawDebug = false; } else if(_drawCubes && !_drawDebug) { _drawCubes = false; _drawDebug = true; } else if(!_drawCubes && _drawDebug) { _drawCubes = true; _drawDebug = true; } /* What to shoot */ } else if(event.key() == KeyEvent::Key::S) { _shootBox ^= true; } else return; event.setAccepted(); } void BulletDummy::mousePressEvent(MouseEvent& event) { /* Shoot an object on click */ if(event.button() == MouseEvent::Button::Left) { /* First scale the position from being relative to window size to being relative to framebuffer size as those two can be different on HiDPI systems */ const Vector2i position = event.position()*Vector2{framebufferSize()}/Vector2{windowSize()}; const Vector2 clickPoint = Vector2::yScale(-1.0f)*(Vector2{position}/Vector2{framebufferSize()} - Vector2{0.5f})*_camera->projectionSize(); const Vector3 direction = (_cameraObject->absoluteTransformation().rotationScaling()*Vector3{clickPoint, -1.0f}).normalized(); auto* object = new RigidBody{ &_scene, _shootBox ? 1.0f : 5.0f, _shootBox ? static_cast(&_bBoxShape) : &_bSphereShape, _bWorld}; object->translate(_cameraObject->absoluteTransformation().translation()); /* Has to be done explicitly after the translate() above, as Magnum -> Bullet updates are implicitly done only for kinematic bodies */ object->syncPose(); /* Create either a box or a sphere */ new ColoredDrawable{*object, _shootBox ? _boxInstanceData : _sphereInstanceData, _shootBox ? 0x880000_rgbf : 0x220000_rgbf, Matrix4::scaling(Vector3{_shootBox ? 0.5f : 0.25f}), _drawables}; /* Give it an initial velocity */ object->rigidBody().setLinearVelocity(btVector3{direction*25.f}); event.setAccepted(); } } }} MAGNUM_APPLICATION_MAIN(Magnum::Examples::BulletDummy) ================================================ FILE: src/examples/picking_objects/CMakeLists.txt ================================================ add_executable(picking_example picking_example.cpp) target_link_libraries(picking_example PRIVATE Corrade::Main Magnum::Application Magnum::GL Magnum::Magnum Magnum::MeshTools Magnum::Primitives Magnum::SceneGraph Magnum::Shaders ) ================================================ FILE: src/examples/picking_objects/picking_example.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Magnum { namespace Examples { using namespace Magnum::Math::Literals; typedef SceneGraph::Object Object3D; typedef SceneGraph::Scene Scene3D; class PickableObject: public Object3D, SceneGraph::Drawable3D { public: explicit PickableObject(UnsignedInt id, Shaders::Phong& shader, const Color3& color, GL::Mesh& mesh, Object3D& parent, SceneGraph::DrawableGroup3D& drawables): Object3D{&parent}, SceneGraph::Drawable3D{*this, &drawables}, _id{id}, _selected{false}, _shader(shader), _color{color}, _mesh(mesh) {} void setSelected(bool selected) { _selected = selected; } private: virtual void draw(const Matrix4& transformationMatrix, SceneGraph::Camera3D& camera) override { _shader.setTransformationMatrix(transformationMatrix) .setNormalMatrix(transformationMatrix.normalMatrix()) .setProjectionMatrix(camera.projectionMatrix()) .setAmbientColor(_selected ? _color*0.3f : Color3{}) .setDiffuseColor(_color*(_selected ? 2.0f : 1.0f)) /* relative to the camera */ .setLightPosition({13.0f, 2.0f, 5.0f}) .setObjectId(_id) .draw(_mesh); } UnsignedInt _id; bool _selected; Shaders::Phong& _shader; Color3 _color; GL::Mesh& _mesh; }; class PickingExample: public Platform::Application { public: explicit PickingExample(const Arguments& arguments); private: void drawEvent() override; void mousePressEvent(MouseEvent& event) override; void mouseMoveEvent(MouseMoveEvent& event) override; void mouseReleaseEvent(MouseEvent& event) override; Scene3D _scene; Object3D* _cameraObject; SceneGraph::Camera3D* _camera; SceneGraph::DrawableGroup3D _drawables; Shaders::Phong _shader{Shaders::Phong::Flag::ObjectId}; GL::Mesh _cube, _plane, _sphere; enum { ObjectCount = 6 }; PickableObject* _objects[ObjectCount]; GL::Framebuffer _framebuffer; GL::Renderbuffer _color, _objectId, _depth; Vector2i _previousMousePosition, _mousePressPosition; }; PickingExample::PickingExample(const Arguments& arguments): Platform::Application{arguments, Configuration{}.setTitle("Magnum object picking_objects example")}, _framebuffer{GL::defaultFramebuffer.viewport()} { MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GL330); /* Global renderer configuration */ GL::Renderer::enable(GL::Renderer::Feature::DepthTest); /* Configure framebuffer. Using a 32-bit int for object ID, which is likely enough. Use a smaller type if you have less objects to save memory. */ _color.setStorage(GL::RenderbufferFormat::RGBA8, GL::defaultFramebuffer.viewport().size()); _objectId.setStorage(GL::RenderbufferFormat::R32UI, GL::defaultFramebuffer.viewport().size()); _depth.setStorage(GL::RenderbufferFormat::DepthComponent24, GL::defaultFramebuffer.viewport().size()); _framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color) .attachRenderbuffer(GL::Framebuffer::ColorAttachment{1}, _objectId) .attachRenderbuffer(GL::Framebuffer::BufferAttachment::Depth, _depth) .mapForDraw({{Shaders::Phong::ColorOutput, GL::Framebuffer::ColorAttachment{0}}, {Shaders::Phong::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}}}); CORRADE_INTERNAL_ASSERT(_framebuffer.checkStatus(GL::FramebufferTarget::Draw) == GL::Framebuffer::Status::Complete); /* Set up meshes */ _cube = MeshTools::compile(Primitives::cubeSolid()); _sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); _plane = MeshTools::compile(Primitives::planeSolid()); /* Set up objects */ (*(_objects[0] = new PickableObject{1, _shader, 0x3bd267_rgbf, _cube, _scene, _drawables})) .rotate(34.0_degf, Vector3(1.0f).normalized()) .translate({1.0f, 0.3f, -1.2f}); (*(_objects[1] = new PickableObject{2, _shader, 0x2f83cc_rgbf, _sphere, _scene, _drawables})) .translate({-1.2f, -0.3f, -0.2f}); (*(_objects[2] = new PickableObject{3, _shader, 0xdcdcdc_rgbf, _plane, _scene, _drawables})) .rotate(278.0_degf, Vector3(1.0f).normalized()) .scale(Vector3(0.45f)) .translate({-1.0f, 1.2f, 1.5f}); (*(_objects[3] = new PickableObject{4, _shader, 0xc7cf2f_rgbf, _sphere, _scene, _drawables})) .translate({-0.2f, -1.7f, -2.7f}); (*(_objects[4] = new PickableObject{5, _shader, 0xcd3431_rgbf, _sphere, _scene, _drawables})) .translate({0.7f, 0.6f, 2.2f}) .scale(Vector3(0.75f)); (*(_objects[5] = new PickableObject{6, _shader, 0xa5c9ea_rgbf, _cube, _scene, _drawables})) .rotate(-92.0_degf, Vector3(1.0f).normalized()) .scale(Vector3(0.25f)) .translate({-0.5f, -0.3f, 1.8f}); /* Configure camera */ _cameraObject = new Object3D{&_scene}; _cameraObject->translate(Vector3::zAxis(8.0f)); _camera = new SceneGraph::Camera3D{*_cameraObject}; _camera->setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend) .setProjectionMatrix(Matrix4::perspectiveProjection(35.0_degf, 4.0f/3.0f, 0.001f, 100.0f)) .setViewport(GL::defaultFramebuffer.viewport().size()); } void PickingExample::drawEvent() { /* Draw to custom framebuffer */ _framebuffer .clearColor(0, Color3{0.125f}) .clearColor(1, Vector4ui{}) .clearDepth(1.0f) .bind(); _camera->draw(_drawables); /* Bind the main buffer back */ GL::defaultFramebuffer.clear(GL::FramebufferClear::Color|GL::FramebufferClear::Depth) .bind(); /* Blit color to window framebuffer */ _framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{0}); GL::AbstractFramebuffer::blit(_framebuffer, GL::defaultFramebuffer, {{}, _framebuffer.viewport().size()}, GL::FramebufferBlit::Color); swapBuffers(); } void PickingExample::mousePressEvent(MouseEvent& event) { if(event.button() != MouseEvent::Button::Left) return; _previousMousePosition = _mousePressPosition = event.position(); event.setAccepted(); } void PickingExample::mouseMoveEvent(MouseMoveEvent& event) { if(!(event.buttons() & MouseMoveEvent::Button::Left)) return; /* We have to take window size, not framebuffer size, since the position is in window coordinates and the two can be different on HiDPI systems */ const Vector2 delta = 3.0f* Vector2{event.position() - _previousMousePosition}/ Vector2{windowSize()}; (*_cameraObject) .rotate(Rad{-delta.y()}, _cameraObject->transformation().right().normalized()) .rotateY(Rad{-delta.x()}); _previousMousePosition = event.position(); event.setAccepted(); redraw(); } void PickingExample::mouseReleaseEvent(MouseEvent& event) { if(event.button() != MouseEvent::Button::Left || _mousePressPosition != event.position()) return; /* First scale the position from being relative to window size to being relative to framebuffer size as those two can be different on HiDPI systems */ const Vector2i position = event.position()*Vector2{framebufferSize()}/Vector2{windowSize()}; const Vector2i fbPosition{position.x(), GL::defaultFramebuffer.viewport().sizeY() - position.y() - 1}; /* Read object ID at given click position */ _framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1}); Image2D data = _framebuffer.read( Range2Di::fromSize(fbPosition, {1, 1}), {PixelFormat::R32UI}); /* Highlight object under mouse and deselect all other */ for(auto* o: _objects) o->setSelected(false); UnsignedInt id = Containers::arrayCast(data.data())[0]; if(id > 0 && id < ObjectCount + 1) _objects[id - 1]->setSelected(true); event.setAccepted(); redraw(); } }} MAGNUM_APPLICATION_MAIN(Magnum::Examples::PickingExample) ================================================ FILE: src/examples/textured_triangle/CMakeLists.txt ================================================ corrade_add_resource(TexturedTriangle_RESOURCES resources.conf) add_executable(textured_triangle textured_triangle.cpp textured_triangle_shader.cpp ${TexturedTriangle_RESOURCES}) target_link_libraries(textured_triangle PRIVATE Corrade::Main Magnum::Application Magnum::GL Magnum::Magnum Magnum::Trade ) # So the TgaImporter gets built implicitly add_dependencies(textured_triangle Magnum::TgaImporter) ================================================ FILE: src/examples/textured_triangle/resources.conf ================================================ group=textured-triangle-data [file] filename=textured_triangle_shader.frag [file] filename=textured_triangle_shader.vert [file] filename=stone.tga ================================================ FILE: src/examples/textured_triangle/textured_triangle.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "textured_triangle_shader.hpp" using namespace Magnum; class TexturedTriangleExample: public Platform::Application { public: explicit TexturedTriangleExample(const Arguments& arguments); private: void drawEvent() override; GL::Mesh _mesh; TexturedTriangleShader _shader; GL::Texture2D _texture; }; TexturedTriangleExample::TexturedTriangleExample(const Arguments& arguments): Platform::Application{arguments, Configuration{} .setTitle("Magnum Textured Triangle Example")} { struct TriangleVertex { Vector2 position; Vector2 textureCoordinates; }; const TriangleVertex data[]{ {{-0.5f, -0.5f}, {0.0f, 0.0f}}, /* Left position and texture coordinate */ {{ 0.5f, -0.5f}, {1.0f, 0.0f}}, /* Right position and texture coordinate */ {{ 0.0f, 0.5f}, {0.5f, 1.0f}} /* Top position and texture coordinate */ }; GL::Buffer buffer; buffer.setData(data); _mesh.setCount(3) .addVertexBuffer(std::move(buffer), 0, TexturedTriangleShader::Position{}, TexturedTriangleShader::TextureCoordinates{}); PluginManager::Manager manager; Containers::Pointer importer = manager.loadAndInstantiate("TgaImporter"); if(!importer) std::exit(1); const Utility::Resource rs{"textured-triangle-data"}; if(!importer->openData(rs.getRaw("stone.tga"))) std::exit(2); Containers::Optional image = importer->image2D(0); CORRADE_INTERNAL_ASSERT(image); _texture.setWrapping(GL::SamplerWrapping::ClampToEdge) .setMagnificationFilter(GL::SamplerFilter::Linear) .setMinificationFilter(GL::SamplerFilter::Linear) .setStorage(1, GL::textureFormat(image->format()), image->size()) .setSubImage(0, {}, *image); } void TexturedTriangleExample::drawEvent() { GL::defaultFramebuffer.clear(GL::FramebufferClear::Color); using namespace Math::Literals; _shader .setColor(0xffb2b2_rgbf) .bindTexture(_texture) .draw(_mesh); swapBuffers(); } MAGNUM_APPLICATION_MAIN(TexturedTriangleExample) ================================================ FILE: src/examples/textured_triangle/textured_triangle_shader.cpp ================================================ #include "textured_triangle_shader.hpp" #include #include #include #include #include namespace Magnum { TexturedTriangleShader::TexturedTriangleShader() { MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GL330); const Utility::Resource rs{"textured-triangle-data"}; GL::Shader vert{GL::Version::GL330, GL::Shader::Type::Vertex}; GL::Shader frag{GL::Version::GL330, GL::Shader::Type::Fragment}; vert.addSource(rs.get("textured_triangle_shader.vert")); frag.addSource(rs.get("textured_triangle_shader.frag")); CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); attachShaders({vert, frag}); CORRADE_INTERNAL_ASSERT_OUTPUT(link()); _colorUniform = uniformLocation("color"); setUniform(uniformLocation("textureData"), TextureUnit); } } ================================================ FILE: src/examples/textured_triangle/textured_triangle_shader.frag ================================================ uniform vec3 color = vec3(1.0, 1.0, 1.0); uniform sampler2D textureData; in vec2 interpolatedTextureCoordinates; out vec4 fragmentColor; void main() { fragmentColor.rgb = color*texture(textureData, interpolatedTextureCoordinates).rgb; fragmentColor.a = 1.0; } ================================================ FILE: src/examples/textured_triangle/textured_triangle_shader.hpp ================================================ #pragma once #include #include #include namespace Magnum { class TexturedTriangleShader: public GL::AbstractShaderProgram { public: typedef GL::Attribute<0, Vector2> Position; typedef GL::Attribute<1, Vector2> TextureCoordinates; explicit TexturedTriangleShader(); TexturedTriangleShader& setColor(const Color3& color) { setUniform(_colorUniform, color); return *this; } TexturedTriangleShader& bindTexture(GL::Texture2D& texture) { texture.bind(TextureUnit); return *this; } private: enum: Int { TextureUnit = 0 }; Int _colorUniform; }; } ================================================ FILE: src/examples/textured_triangle/textured_triangle_shader.vert ================================================ layout(location = 0) in vec4 position; layout(location = 1) in vec2 textureCoordinates; out vec2 interpolatedTextureCoordinates; void main() { interpolatedTextureCoordinates = textureCoordinates; gl_Position = position; } ================================================ FILE: src/examples/triangle.cpp ================================================ #include #include #include #include #include #include using namespace Magnum; class TriangleExample: public Platform::Application { public: explicit TriangleExample(const Arguments& arguments); private: void drawEvent() override; GL::Mesh _mesh; Shaders::VertexColor2D _shader; }; TriangleExample::TriangleExample(const Arguments& arguments): Platform::Application{arguments, Configuration{}.setTitle("Magnum Triangle Example")} { using namespace Math::Literals; struct TriangleVertex { Vector2 position; Color3 color; }; const TriangleVertex data[]{ {{-0.5f, -0.5f}, 0xff0000_rgbf}, /* Left vertex, red color */ {{ 0.5f, -0.5f}, 0x00ff00_rgbf}, /* Right vertex, green color */ {{ 0.0f, 0.5f}, 0x0000ff_rgbf} /* Top vertex, blue color */ }; GL::Buffer buffer; buffer.setData(data); _mesh.setCount(3) .addVertexBuffer(std::move(buffer), 0, Shaders::VertexColor2D::Position{}, Shaders::VertexColor2D::Color3{}); } void TriangleExample::drawEvent() { GL::defaultFramebuffer.clear(GL::FramebufferClear::Color); _shader.draw(_mesh); swapBuffers(); } MAGNUM_APPLICATION_MAIN(TriangleExample) ================================================ FILE: src/libs/CMakeLists.txt ================================================ find_package(Corrade REQUIRED Main) add_subdirectory(util) add_subdirectory(rendering) add_subdirectory(magnum_rendering) if (NOT CORRADE_TARGET_APPLE) add_subdirectory(v4r_rendering) endif () add_subdirectory(env) add_subdirectory(mazes) add_subdirectory(scenarios) add_subdirectory(viewer) add_subdirectory(bindings) ================================================ FILE: src/libs/bindings/CMakeLists.txt ================================================ find_package(Corrade REQUIRED Main) if (BUILD_GUI_APPS) add_definitions(-DWITH_GUI=1) endif() pybind11_add_module(megaverse megaverse.cpp) target_link_libraries(megaverse PUBLIC scenarios magnum_rendering) if (NOT CORRADE_TARGET_APPLE) target_link_libraries(megaverse PUBLIC v4r_rendering) endif () if (BUILD_GUI_APPS) target_link_libraries(megaverse PUBLIC viewer Magnum::Application) endif() ================================================ FILE: src/libs/bindings/megaverse.cpp ================================================ #include #include #include #include #include #include #include #include #ifndef CORRADE_TARGET_APPLE #include #endif #ifdef WITH_GUI #include #endif namespace py = pybind11; using namespace Megaverse; void setMegaverseLogLevel(int level) { setLogLevel(LogLevel(level)); } class MegaverseGym { public: MegaverseGym( const std::string &scenario, int w, int h, int numEnvs, int numAgentsPerEnv, int numSimulationThreads, bool useVulkan, const std::map &floatParams ) : numEnvs{numEnvs} , numAgentsPerEnv{numAgentsPerEnv} , useVulkan{useVulkan} , w{w} , h{h} , numSimulationThreads{numSimulationThreads} { scenariosGlobalInit(); for (int i = 0; i < numEnvs; ++i) envs.emplace_back(std::make_unique(scenario, numAgentsPerEnv, floatParams)); rewards = std::vector(size_t(numEnvs * numAgentsPerEnv)); } void seed(int seedValue) { TLOG(INFO) << "Seeding vector env with seed value " << seedValue; rng.seed((unsigned long) seedValue); for (auto &e : envs) { const auto noise = randRange(0, 1 << 30, rng); e->seed(noise); } } int numAgents() const { return envs.front()->getNumAgents(); } void reset() { if (!vectorEnv) { if (useVulkan) #ifdef CORRADE_TARGET_APPLE TLOG(ERROR) << "Vulkan not supported on MacOS"; #else renderer = std::make_unique(envs, w, h, nullptr, false); #endif else renderer = std::make_unique(envs, w, h); vectorEnv = std::make_unique(envs, *renderer, numSimulationThreads); } // this also resets the main renderer vectorEnv->reset(); } std::vector actionSpaceSizes() const { return Env::actionSpaceSizes; } void setActions(int envIdx, int agentIdx, std::vector actions) { int actionIdx = 0, actionMask = 0; const auto &spaces = Env::actionSpaceSizes; for (int i = 0; i < int(actions.size()); ++i) { const auto action = actions[i]; if (action > 0) actionMask = actionMask | (1 << (actionIdx + action)); const auto numNonIdleActions = spaces[i] - 1; actionIdx += numNonIdleActions; } envs[envIdx]->setAction(agentIdx, Action(actionMask)); } void step() { vectorEnv->step(); } bool isDone(int envIdx) { return vectorEnv->done[envIdx]; } std::vector getLastRewards() { int i = 0; for (int envIdx = 0; envIdx < numEnvs; ++envIdx) for (int agentIdx = 0; agentIdx < numAgentsPerEnv; ++agentIdx) rewards[i++] = envs[envIdx]->getLastReward(agentIdx); return rewards; } py::array_t getObservation(int envIdx, int agentIdx) { const uint8_t *obsData = renderer->getObservation(envIdx, agentIdx); return py::array_t({h, w, 4}, obsData, py::none{}); // numpy object does not own memory } /** * Call this before the first call to render() */ void setRenderResolution(int hiresW, int hiresH) { renderW = hiresW; renderH = hiresH; } void drawHires() { if (!hiresRenderer) { if (useVulkan) #ifdef CORRADE_TARGET_APPLE TLOG(ERROR) << "Vulkan not supported on MacOS"; #else hiresRenderer = std::make_unique(envs, renderW, renderH, dynamic_cast(renderer.get()), true); #endif else hiresRenderer = std::make_unique(envs, renderW, renderH); for (int envIdx = 0; envIdx < int(envs.size()); ++envIdx) hiresRenderer->reset(*envs[envIdx], envIdx); } for (int envIdx = 0; envIdx < int(envs.size()); ++envIdx) { if (isDone(envIdx)) hiresRenderer->reset(*envs[envIdx], envIdx); hiresRenderer->preDraw(*envs[envIdx], envIdx); } hiresRenderer->draw(envs); } void drawOverview() { #ifdef WITH_GUI if (!viewer && Viewer::viewerExists) { TLOG(INFO) << "Only one viewer per process is supported"; return; } if (!viewer) { static int argc = 1; static const char *argv[] = {"Overview"}; Magnum::Platform::Application::Arguments fakeArgs{argc, (char **) argv}; TLOG(INFO) << __FUNCTION__ << " Creating a viewer object"; viewer = std::make_unique(envs, useVulkan, renderer.get(), fakeArgs); } viewer->step(vectorEnv->done); viewer->mainLoopIteration(); // handle events, update the window, that kind of thing #else // TLOG(ERROR) << "Megaverse was built without GUI support"; #endif } py::array_t getHiresObservation(int envIdx, int agentIdx) { const uint8_t *obsData = hiresRenderer->getObservation(envIdx, agentIdx); return py::array_t({renderH, renderW, 4}, obsData, py::none{}); // numpy object does not own memory } float trueObjective(int envIdx, int agentIdx) const { return vectorEnv->trueObjectives[envIdx][agentIdx]; } std::map getRewardShaping(int envIdx, int agentIdx) { return envs[envIdx]->getScenario().getRewardShaping(agentIdx); } void setRewardShaping(int envIdx, int agentIdx, const std::map &rewardShaping) { envs[envIdx]->getScenario().setRewardShaping(agentIdx, rewardShaping); } /** * Explicitly destroy the env and the renderer to avoid doing this when the Python object goes out-of-scope. */ void close() { if (vectorEnv) vectorEnv->close(); #ifdef WITH_GUI if (viewer) viewer->exit(0); viewer.reset(); #endif hiresRenderer.reset(); renderer.reset(); vectorEnv.reset(); envs.clear(); } private: Envs envs; int numEnvs, numAgentsPerEnv; std::vector rewards; // to avoid reallocating on every call std::unique_ptr vectorEnv; std::unique_ptr renderer, hiresRenderer; Rng rng{std::random_device{}()}; #ifdef WITH_GUI std::unique_ptr viewer; #endif bool useVulkan; int w, h; int renderW = 768, renderH = 432; int numSimulationThreads; }; PYBIND11_MODULE(megaverse, m) { m.doc() = "Megaverse Python bindings"; // optional module docstring m.def("set_megaverse_log_level", &setMegaverseLogLevel, "Megaverse Log Level (0 to disable all logs, 2 for warnings"); py::class_(m, "MegaverseGym") .def(py::init()) .def("num_agents", &MegaverseGym::numAgents) .def("action_space_sizes", &MegaverseGym::actionSpaceSizes) .def("seed", &MegaverseGym::seed) .def("reset", &MegaverseGym::reset) .def("set_actions", &MegaverseGym::setActions) .def("step", &MegaverseGym::step) .def("is_done", &MegaverseGym::isDone) .def("get_observation", &MegaverseGym::getObservation) .def("get_last_rewards", &MegaverseGym::getLastRewards) .def("true_objective", &MegaverseGym::trueObjective) .def("set_render_resolution", &MegaverseGym::setRenderResolution) .def("draw_hires", &MegaverseGym::drawHires) .def("draw_overview", &MegaverseGym::drawOverview) .def("get_hires_observation", &MegaverseGym::getHiresObservation) .def("get_reward_shaping", &MegaverseGym::getRewardShaping) .def("set_reward_shaping", &MegaverseGym::setRewardShaping) .def("close", &MegaverseGym::close); } ================================================ FILE: src/libs/env/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.10) project(libenv VERSION 0.1 LANGUAGES CXX) set(MAGNUM_DEPENDENCIES Corrade::Main Magnum::GL Magnum::Magnum Magnum::SceneGraph MagnumIntegration::Bullet ) add_library_default(env) target_link_libraries(env PUBLIC util ${MAGNUM_DEPENDENCIES}) ================================================ FILE: src/libs/env/include/env/agent.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include namespace Megaverse { class AbstractAgent : public Object3D { public: explicit AbstractAgent(Object3D *parent, btDynamicsWorld &bWorld, float verticalLookLimitRad); virtual void updateTransform() = 0; virtual void lookLeft(float dt) = 0; virtual void lookRight(float dt) = 0; virtual void lookUp(float dt) = 0; virtual void lookDown(float dt) = 0; virtual btVector3 forwardDirection() const = 0; virtual btVector3 strafeLeftDirection() const = 0; virtual bool onGround() const = 0; virtual void accelerate(const btVector3 &acc, btScalar frameDuration) = 0; virtual void jump() = 0; virtual void teleport(const btVector3 &position) = 0; virtual float getAgentHeight() = 0; virtual Magnum::SceneGraph::Camera3D * getCamera() = 0; virtual Object3D * getCameraObject() = 0; virtual Object3D * interactLocation() = 0; private: virtual void rotateYAxis(float radians) = 0; protected: float verticalLookLimitRad = 0.0f; btDynamicsWorld &bWorld; }; class DefaultKinematicAgent : public AbstractAgent { public: explicit DefaultKinematicAgent( Object3D *parent, btDynamicsWorld &bWorld, const Magnum::Vector3 &startingPosition, float rotationRad, float verticalLookLimitRad ); ~DefaultKinematicAgent() override; void updateTransform() override; void lookLeft(float dt) override; void lookRight(float dt) override; void lookUp(float dt) override; void lookDown(float dt) override; btVector3 forwardDirection() const override; btVector3 strafeLeftDirection() const override; bool onGround() const override; void accelerate(const btVector3 &acc, btScalar frameDuration) override; void jump() override; void teleport(const btVector3 &position) override; float getAgentHeight() override { return agentHeight; } Magnum::SceneGraph::Camera3D * getCamera() override { return camera; } Object3D * getCameraObject() override { return cameraObject; } Object3D * interactLocation() override { return pickupSpot; } private: void rotateYAxis(float radians) override; private: static constexpr auto rotateRadians = 3.5f, rotateXRadians = 1.5f; static constexpr auto agentHeight = 1.75f; float currXRotation = 0.0f; Object3D *cameraObject; Magnum::SceneGraph::Camera3D *camera; Object3D *pickupSpot; std::unique_ptr capsuleShape; btPairCachingGhostObject ghostObject; std::unique_ptr bCharacter; }; } ================================================ FILE: src/libs/env/include/env/const.hpp ================================================ #pragma once #include #include namespace Megaverse { using ConstStr = const char *const; namespace Str { ConstStr teamSpirit = "teamSpirit"; ConstStr episodeLengthSec = "episodeLengthSec", verticalLookLimitRad = "verticalLookLimitRad", useUIRewardIndicators = "useUIRewardIndicators"; } /** * Enum with default colors */ enum class ColorRgb { YELLOW = 0xffdd3c, GREEN = 0x3bb372, LIGHT_GREEN = 0x50c878, BLUE = 0x2eb5d0, LIGHT_BLUE = 0xadd8e6, DARK_BLUE = 0x3a7fa6, DARK_NAVY = 0x2c3e50, ORANGE = 0xffb400, GREY = 0xb3b3b3, DARK_GREY = 0x555555, VERY_DARK_GREY = 0x222222, WHITE = 0xffffff, RED = 0xff0000, LIGHT_ORANGE = 0xffa770, VIOLET = 0xd468ee, LIGHT_PINK = 0xffe6e6, VERY_LIGHT_YELLOW = 0xffffe6, VERY_LIGHT_GREEN = 0xccffcc, VERY_LIGHT_BLUE = 0xe6ecff, VERY_LIGHT_GREY = 0xd9d9d9, VERY_LIGHT_VIOLET = 0xf2e6ff, VERY_LIGHT_ORANGE = 0xffebcc, LAYOUT_DEFAULT = WHITE, AGENT_EYES = DARK_NAVY, MOVABLE_BOX = LIGHT_BLUE, EXIT_PAD = LIGHT_GREEN, BUILDING_ZONE = DARK_GREY, }; const ColorRgb allColors[] = { ColorRgb::YELLOW, ColorRgb::GREEN, ColorRgb::LIGHT_GREEN, ColorRgb::BLUE, ColorRgb::LIGHT_BLUE, ColorRgb::DARK_BLUE, ColorRgb::DARK_NAVY, ColorRgb::ORANGE, ColorRgb::GREY, ColorRgb::DARK_GREY, ColorRgb::VERY_DARK_GREY, ColorRgb::WHITE, ColorRgb::RED, ColorRgb::LIGHT_ORANGE, ColorRgb::VIOLET, ColorRgb::LIGHT_PINK, ColorRgb::VERY_LIGHT_YELLOW, ColorRgb::VERY_LIGHT_GREEN, ColorRgb::VERY_LIGHT_BLUE, ColorRgb::VERY_LIGHT_GREY, ColorRgb::VERY_LIGHT_VIOLET, ColorRgb::VERY_LIGHT_ORANGE, }; const int numColors = ARR_LENGTH(allColors); const ColorRgb agentColors[] = {ColorRgb::YELLOW, ColorRgb::GREEN, ColorRgb::BLUE, ColorRgb::ORANGE, ColorRgb::VIOLET, ColorRgb::VERY_DARK_GREY, ColorRgb::RED}; const int numAgentColors = ARR_LENGTH(agentColors); inline Magnum::Color3 rgb(ColorRgb color) { return toRgbf((unsigned long long)color); } inline ColorRgb sampleRandomColor(Rng &rng) { const auto idx = randRange(0, numColors, rng); return allColors[idx]; } const ColorRgb objectColors[] = { ColorRgb::YELLOW, ColorRgb::GREEN, ColorRgb::LIGHT_GREEN, ColorRgb::BLUE, ColorRgb::LIGHT_BLUE, ColorRgb::DARK_BLUE, ColorRgb::ORANGE, ColorRgb::GREY, ColorRgb::DARK_GREY, ColorRgb::WHITE, ColorRgb::RED, ColorRgb::LIGHT_ORANGE, ColorRgb::VIOLET, ColorRgb::LIGHT_PINK, }; const int numObjectColors = ARR_LENGTH(objectColors); inline ColorRgb randomObjectColor(Rng &rng) { const auto idx = randRange(0, numObjectColors, rng); return objectColors[idx]; } const ColorRgb layoutColors[] = { ColorRgb::LAYOUT_DEFAULT, ColorRgb::VERY_LIGHT_YELLOW, ColorRgb::VERY_LIGHT_GREEN, ColorRgb::VERY_LIGHT_BLUE, ColorRgb::VERY_LIGHT_GREY, ColorRgb::VERY_LIGHT_ORANGE, ColorRgb::GREY, ColorRgb::GREY, ColorRgb::GREY, ColorRgb::GREY, ColorRgb::DARK_GREY, ColorRgb::DARK_GREY, ColorRgb::DARK_GREY, ColorRgb::DARK_GREY, }; const int numLayoutColors = ARR_LENGTH(layoutColors); inline ColorRgb randomLayoutColor(Rng &rng) { const auto idx = randRange(0, numLayoutColors, rng); return layoutColors[idx]; } } ================================================ FILE: src/libs/env/include/env/env.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include namespace Megaverse { class Scenario; enum class Action { Idle = 0, Left = 1 << 1, Right = 1 << 2, Forward = 1 << 3, Backward = 1 << 4, LookLeft = 1 << 5, LookRight = 1 << 6, Jump = 1 << 7, Interact = 1 << 8, LookDown = 1 << 9, LookUp = 1 << 10, NumActions = 11, }; inline Action operator|(Action a, Action b) { return Action(int(a) | int(b)); } inline Action &operator|=(Action &a, Action b) { return (Action &) ((int &) a |= int(b)); } inline Action operator&(Action a, Action b) { return Action(int(a) & int(b)); } inline Action &operator&=(Action &a, Action b) { return (Action &) ((int &) a &= int(b)); } inline Action operator~(Action a) { return Action(~int(a)); } inline bool operator!(Action a) { return a == Action::Idle; } enum class DrawableType { First = 0, Box = 0, Capsule = 1, Sphere = 2, Cone = 3, Cylinder = 4, NumTypes, }; struct SceneObjectInfo { SceneObjectInfo(Object3D *objectPtr, const Magnum::Color3 &color) : objectPtr{objectPtr} , color{color} { } Object3D *objectPtr; Magnum::Color3 color; }; using FloatParams = std::map; using Agents = std::vector; using DrawablesMap = std::map>; using RewardShaping = std::map; class Env { public: /** * Physics-related fields (Bullet physics engine) */ struct EnvPhysics { EnvPhysics() { // what does this really do? bBroadphase.getOverlappingPairCache()->setInternalGhostPairCallback(&ghostPairCallback); } ~EnvPhysics() { collisionShapes.clear(); } btGhostPairCallback ghostPairCallback; btDbvtBroadphase bBroadphase; btSequentialImpulseConstraintSolver bConstraintSolver; btDefaultCollisionConfiguration bCollisionConfiguration; btCollisionDispatcher bCollisionDispatcher{&bCollisionConfiguration}; btDiscreteDynamicsWorld bWorld{&bCollisionDispatcher, &bBroadphase, &bConstraintSolver, &bCollisionConfiguration}; std::vector> collisionShapes; }; /** * Current state of the environment. * This is what other components (e.g. Scenario) will get access to. */ struct EnvState { public: explicit EnvState(int numAgents) : physics{std::make_unique()} , currAction(size_t(numAgents), Action::Idle) , lastReward(size_t(numAgents), 0) , totalReward(size_t(numAgents), 0.0f) { } void reset() { done = false; currEpisodeSec = 0; numFrames = 0; std::fill(currAction.begin(), currAction.end(), Action::Idle); std::fill(lastReward.begin(), lastReward.end(), 0.0f); std::fill(totalReward.begin(), totalReward.end(), 0.0f); scene = std::make_unique(); agents.clear(); // completely reset the whole simulation physics = std::make_unique(); } public: std::unique_ptr physics; // Basic environment info bool done = false; int numFrames = 0; float currEpisodeSec = 0; float simulationStepSeconds = 1.0f / 15.0f; // 15 FPS is default float lastFrameDurationSec = simulationStepSeconds; std::vector currAction; std::vector lastReward, totalReward; std::unique_ptr scene; Agents agents; Rng rng{std::random_device{}()}; }; public: explicit Env(const std::string &scenarioName, int numAgents = 2, const FloatParams& customFloatParams = FloatParams{}); ~Env(); int getNumAgents() const { return numAgents; } Scenario & getScenario() { return *scenario; } Scene3D & getScene() const { return *state.scene; } Agents & getAgents() { return state.agents; } EnvPhysics & getPhysics() const { return *state.physics; } /** * Main interface between the env and the renderer. * @return the list of drawables for each geometric shape supported. */ const DrawablesMap & getDrawables() const { return drawables; } void reset(); /** * Set action for the next tick. * @param agentIdx index of the agent for which we're setting the action * @param action action mask (see enum) */ void setAction(int agentIdx, Action action); /** * Advance simulation by one step. */ void step(); bool isDone() const { return state.done; } /** * @param agentIdx agent for which to query the last reward * @return reward in the last tick */ float getLastReward(int agentIdx) const { return state.lastReward[agentIdx]; } float getTotalReward(int agentIdx) const { return state.totalReward[agentIdx]; } /** * Unshaped reward that we're actually trying to maximize. */ float trueObjective(int agentIdx) const; float episodeLengthSec() const; float remainingTimeFraction() const { const auto len = episodeLengthSec(); return std::max(0.0f, (len - state.currEpisodeSec) / len); } void terminateEpisodeOnNextFrame(); /** * We need this because of the requirements of the Vulkan renderer (materials have to be known in advance) */ std::vector getPalette() const; /** * Seed the rng with specific seed value. */ void seed(int seedValue); Rng &getRng() { return state.rng; } /** * This is when we're running an actual realtime rendering loop with human controls. * Should not be used by Gym env interface. * @param sec actual duration of the last frame. */ void setFrameDuration(float sec) { state.lastFrameDurationSec = sec; } void setSimulationResolution(float sec) { state.simulationStepSeconds = sec; } public: // need better mechanism for this static const std::vector actionSpaceSizes; private: std::string scenarioName; std::unique_ptr scenario; EnvState state; int numAgents; DrawablesMap drawables; }; using Envs = std::vector>; } ================================================ FILE: src/libs/env/include/env/env_renderer.hpp ================================================ #pragma once #include namespace Megaverse { // defined later in render_utils.cpp class Overview; class EnvRenderer { public: virtual ~EnvRenderer() = default; virtual void reset(Env &env, int envIdx) = 0; virtual void preDraw(Env &env, int envIndex) = 0; virtual void draw(Envs &envs) = 0; /** * Query the pointer to memory holding the latest observation for an agent in an env. * @param envIdx env index. * @param agentIdx agent for which we're querying the observation. * @return pointer to the memory holding the observation. */ virtual const uint8_t *getObservation(int envIdx, int agentIdx) const = 0; virtual Overview * getOverview() = 0; }; inline std::tuple agentCameraParameters() { float fov = 100, near = 0.01, far = 120.0, aspectRatio = 128.0f / 72.0f; return std::make_tuple(fov, near, far, aspectRatio); } inline std::tuple overviewCameraParameters() { auto [fov, near, far, aspectRatio] = agentCameraParameters(); fov = 100, near = 0.1, far = 600.0; return std::make_tuple(fov, near, far, aspectRatio); } } ================================================ FILE: src/libs/env/include/env/kinematic_character_controller.hpp ================================================ #pragma once #include "LinearMath/btVector3.h" #include "BulletDynamics/Dynamics/btActionInterface.h" #include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" class btCollisionShape; class btConvexShape; class btRigidBody; class btCollisionWorld; class btCollisionDispatcher; class btPairCachingGhostObject; namespace Megaverse { ///btKinematicCharacterController is an object that supports a sliding motion in a world. ///It uses a ghost object and convex sweep test to test for upcoming collisions. This is combined with discrete collision detection to recover from penetrations. ///Interaction between btKinematicCharacterController and dynamic rigid bodies needs to be explicity implemented by the user. ATTRIBUTE_ALIGNED16(class) KinematicCharacterController : public btActionInterface { public: BT_DECLARE_ALIGNED_ALLOCATOR(); KinematicCharacterController(btPairCachingGhostObject *ghostObject, btConvexShape *convexShape, btScalar stepHeight, const btVector3 &up = btVector3(1.0, 0.0, 0.0)); ~KinematicCharacterController() override; ///btActionInterface interface void updateAction(btCollisionWorld *collisionWorld, btScalar deltaTime) override { preStep(collisionWorld); playerStep(collisionWorld, deltaTime); } ///btActionInterface interface void debugDraw(btIDebugDraw *debugDrawer) override; void setUp(const btVector3 &up); const btVector3 &getUp() { return m_up; } /// This should probably be called setPositionIncrementPerSimulatorStep. /// This is neither a direction nor a velocity, but the amount to /// increment the position each simulation iteration, regardless /// of dt. /// This call will reset any velocity set by setVelocityForTimeInterval(). virtual void setWalkDirection(const btVector3 &walkDirection); virtual void setAngularVelocity(const btVector3 &velocity); virtual const btVector3 &getAngularVelocity() const; virtual void setLinearVelocity(const btVector3 &velocity); virtual btVector3 getLinearVelocity() const; void setAngularDamping(btScalar d) { m_angularDamping = btClamped(d, (btScalar) btScalar(0.0), (btScalar) btScalar(1.0)); } btScalar getAngularDamping() const { return m_angularDamping; } void reset(btCollisionWorld *collisionWorld); void warp(const btVector3 &origin); void preStep(btCollisionWorld *collisionWorld); void playerStep(btCollisionWorld *collisionWorld, btScalar dt); void setStepHeight(btScalar h); btScalar getStepHeight() const { return m_stepHeight; } void setFallSpeed(btScalar fallSpeed); btScalar getFallSpeed() const { return m_fallSpeed; } void setJumpSpeed(btScalar jumpSpeed); btScalar getJumpSpeed() const { return m_jumpSpeed; } void setMaxJumpHeight(btScalar maxJumpHeight); bool canJump() const; void jump(const btVector3 &v = btVector3(0, 0, 0)); void applyImpulse(const btVector3 &v) { jump(v); } void setGravity(const btVector3 &gravity); btVector3 getGravity() const; /// The max slope determines the maximum angle that the controller can walk up. /// The slope angle is measured in radians. void setMaxSlope(btScalar slopeRadians); btScalar getMaxSlope() const; void setMaxPenetrationDepth(btScalar d); btScalar getMaxPenetrationDepth() const; btPairCachingGhostObject *getGhostObject(); void setUseGhostSweepTest(bool useGhostObjectSweepTest) { m_useGhostObjectSweepTest = useGhostObjectSweepTest; } bool onGround() const; void setUpInterpolate(bool value); void setAcceleration(btVector3 acc, btScalar dt); protected: static btVector3 *getUpAxisDirections(); btVector3 computeReflectionDirection(const btVector3 &direction, const btVector3 &normal); btVector3 parallelComponent(const btVector3 &direction, const btVector3 &normal); btVector3 perpindicularComponent(const btVector3 &direction, const btVector3 &normal); bool recoverFromPenetration(btCollisionWorld *collisionWorld, int iteration); void stepUp(btCollisionWorld *collisionWorld); void updateTargetPositionBasedOnCollision(const btVector3 &hit_normal, btScalar closestHitFraction); void stepForwardAndStrafe(btCollisionWorld *collisionWorld, const btVector3 &horizontalVelocity, btScalar dt); void stepDown(btCollisionWorld *collisionWorld, btScalar dt); virtual bool needsCollision(const btCollisionObject *body0, const btCollisionObject *body1); void setUpVector(const btVector3 &up); btQuaternion getRotation(btVector3 &v0, btVector3 &v1) const; protected: btScalar m_halfHeight; btPairCachingGhostObject *m_ghostObject; btConvexShape *m_convexShape; //is also in m_ghostObject, but it needs to be convex, so we store it here to avoid upcast btScalar m_maxPenetrationDepth = 0.041f; btScalar m_verticalVelocity; btScalar m_verticalOffset; btScalar m_fallSpeed; btScalar m_jumpSpeed; btScalar m_SetjumpSpeed; btScalar m_maxJumpHeight; btScalar m_maxSlopeRadians; // Slope angle that is set (used for returning the exact value) btScalar m_maxSlopeCosine; // Cosine equivalent of m_maxSlopeRadians (calculated once when set, for optimization) btScalar m_turnAngle; btScalar m_stepHeight; btScalar m_gravity = 1.4f * 9.8f; btVector3 horizontalVelocity; btScalar maxHorizontalSpeed = 4.5f; // max walking speed, can be exceeded through other events (explosion, impulse) btScalar maxAirSpeed = 1.0f; btScalar normalDeceleration = 15.0f; btScalar maxAcceleration = 35.0f + normalDeceleration, maxAirAcceleration = 3.0f; btScalar exceedingSpeedLimitDeceleration = maxAcceleration * 2; btVector3 m_AngVel; btVector3 m_jumpPosition; //some internal variables btVector3 m_currentPosition; btScalar m_currentStepOffset; btVector3 m_targetPosition; btQuaternion m_currentOrientation; btQuaternion m_targetOrientation; ///keep track of the contact manifolds btManifoldArray m_manifoldArray; bool m_touchingContact; btVector3 m_touchingNormal; btScalar m_angularDamping; bool m_wasOnGround; bool m_wasJumping; bool m_useGhostObjectSweepTest; btVector3 m_up; btVector3 m_jumpAxis; bool m_interpolateUp; }; } ================================================ FILE: src/libs/env/include/env/physics.hpp ================================================ #pragma once #include #include #include #include #include #include #include namespace Megaverse { class RigidBody : public Object3D { public: RigidBody(Object3D *parent, Magnum::Float mass, btCollisionShape *bShape, btDynamicsWorld &bWorld) : Object3D{parent}, bWorld{bWorld} { // calculate inertia so the object reacts as it should with rotation and everything btVector3 bInertia(0.0f, 0.0f, 0.0f); if (!Magnum::Math::TypeTraits::equals(mass, 0.0f)) bShape->calculateLocalInertia(mass, bInertia); // bullet rigid body setup motionState = std::make_unique(*this); bRigidBody = std::make_unique(btRigidBody::btRigidBodyConstructionInfo{mass, &motionState->btMotionState(), bShape, bInertia}); bRigidBody->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); // bRigidBody->forceActivationState(DISABLE_DEACTIVATION); // do we need this? bWorld.addRigidBody(bRigidBody.get()); } ~RigidBody() override { // similar to Bullet demos, delete motion state first, then remove rigid body from the world, and then delete the rigid body motionState.reset(); bWorld.removeRigidBody(bRigidBody.get()); bRigidBody.reset(); } btRigidBody &rigidBody() { return *bRigidBody; } bool colliding() const { return !(bRigidBody->getCollisionFlags() & btCollisionObject::CF_NO_CONTACT_RESPONSE); } /** * Allows to set collsion shape scale relative to the object scale */ void setCollisionScale(const Magnum::Vector3 &scale) { collisionScale = scale; } void setCollisionOffset(const Magnum::Vector3 &offset) { collisionOffset = offset; } /* needed after changing the pose from Magnum side */ void syncPose() { const auto &m = absoluteTransformationMatrix(); bRigidBody->setWorldTransform(btTransform{btMatrix3x3{m.rotation()}, btVector3{m.translation() + collisionOffset}}); bRigidBody->getCollisionShape()->setLocalScaling(btVector3{m.scaling() * collisionScale}); } void toggleCollision() { const auto flags = bRigidBody->getCollisionFlags(); if (flags & btCollisionObject::CF_NO_CONTACT_RESPONSE) { // no collisions for this object, enabling collisions... bRigidBody->setCollisionFlags(flags & ~btCollisionObject::CF_NO_CONTACT_RESPONSE); } else { bRigidBody->setCollisionFlags(flags | btCollisionObject::CF_NO_CONTACT_RESPONSE); } } private: btDynamicsWorld &bWorld; std::unique_ptr bRigidBody; std::unique_ptr motionState; Magnum::Vector3 collisionScale{1, 1, 1}; Magnum::Vector3 collisionOffset; }; } ================================================ FILE: src/libs/env/include/env/scenario.hpp ================================================ #pragma once #include #include #include #include #include #include #include namespace Megaverse { class ScenarioComponent; class Scenario { friend class ScenarioComponent; public: using ScenarioPtr = std::unique_ptr; using FactoryFunc = ScenarioPtr (*)(const std::string &, Env &, Env::EnvState &); using ScenarioRegistry = std::map; public: explicit Scenario(std::string scenarioName, Env &env, Env::EnvState &envState) : scenarioName{std::move(scenarioName)} , env{env} , envState{envState} , rewardShaping(size_t(env.getNumAgents())) { } virtual ~Scenario() = default; /** * Scenario registry and factory methods for instantiating custom scenarios. */ public: static void registerScenario(const std::string &scenarioName, FactoryFunc factoryFunc) { auto &scenarioRegistry = getScenarioRegistry(); const auto scenarioNameLowercase = toLower(scenarioName); scenarioRegistry[scenarioNameLowercase] = factoryFunc; TLOG(INFO) << "Scenario " << scenarioNameLowercase << " registered! " << scenarioRegistry.size() << " scenarios."; } static std::vector registeredScenarios() { auto &scenarioRegistry = getScenarioRegistry(); std::vector keys; keys.reserve(scenarioRegistry.size()); for (const auto& [key, _] : scenarioRegistry) keys.emplace_back(key); return keys; } static ScenarioPtr create(const std::string &scenarioName, Env &env, Env::EnvState &envState) { const auto scenarioNameLowercase = toLower(scenarioName); const auto &scenarioRegistry = getScenarioRegistry(); // TLOG(INFO) << "Num scenarios " << scenarioRegistry.size(); // TLOG(INFO) << "Registry ptr " << &scenarioRegistry; // for (const auto &[k,v]: scenarioRegistry) // TLOG(INFO) << k; if (!scenarioRegistry.count(scenarioNameLowercase)) TLOG(FATAL) << "Unknown scenario " << scenarioNameLowercase << ". Did you register the scenario in scenariosGlobalInit()?"; const auto factoryFunc = scenarioRegistry.at(scenarioNameLowercase); return factoryFunc(scenarioNameLowercase, env, envState); } template static std::unique_ptr scenarioFactory(const std::string &scenarioName, Env &env, Env::EnvState &envState) { ScenarioPtr p = std::make_unique(scenarioName, env, envState); return p; } /** * Interface between environment and scenario objects. */ public: /** * Called once after construction. * We can't call virtual methods in the ctor, therefore this method can be useful. */ virtual void init() { initializeDefaultParameters(); for (int i = 0; i < env.getNumAgents(); ++i) rewardShaping[i] = {{Str::teamSpirit, 0.0f}}; // initialize default reward shaping specific for this scenario initRewardShaping(); } /** * Called on the episode boundary. */ virtual void reset() {} /** * Easy way to terminate the episode a bit more smoothly, rather than abruptly (i.e. on the same frame the last * reward was collected). */ void doneWithTimer(float remainingTimeSeconds=0.3f) { envState.currEpisodeSec = std::max(envState.currEpisodeSec, episodeLengthSec() - remainingTimeSeconds); } /** * This is called by the environment before the physics simulation step. */ virtual void preStep() {} /** * This is called by the environment after the physics simulation step. * This method should normally contain main logic of the scenario. */ virtual void step() {} /** * Called after every step() by the environment, use to adjust health bars, etc. */ virtual void updateUI() {} /** * @return vector with starting positions of the agents. */ virtual std::vector agentStartingPositions() = 0; /** * Called by the env to generate the agent objects for the episode. */ virtual void spawnAgents(std::vector &) = 0; /** * Called by the env to query the set of drawables for the episode. */ virtual void addEpisodeDrawables(DrawablesMap &) {} /** * Same as above, but for drawables related to agents */ virtual void addEpisodeAgentsDrawables(DrawablesMap &) {} /** * We display a rudimentary HUD just by drawing a bunch of geometric primitives very close to the camera. */ virtual void addUIDrawables(DrawablesMap &) {} /** * @return a set of colors used by the renderer in this scenario. */ virtual std::vector getPalette() const = 0; /** * @return unshaped reward for the current episode (i.e. 1 for success 0 for failure) * Typically called only on the last frame of the episode, when done=True */ virtual float trueObjective(int agentIdx) const = 0; /** * @return episode duration in seconds, this can be overridden */ virtual float episodeLengthSec() const { const auto episodeLengthSec = getFloatParams().at(Str::episodeLengthSec); return episodeLengthSec; } /** * Each environment should provide the reward shaping dictionary which allows changing rewards through API * (even during training) */ virtual RewardShaping defaultRewardShaping() const = 0; /** * Utility function, used by initRewardShaping() implementations. * Replaces rewards in rewardShaping with those provided by rs (or adds them to the map if they don't exist). */ void initRewards(const RewardShaping &rs) { for (int i = 0; i < env.getNumAgents(); ++i) for (const auto &[rewardName, value] : rs) rewardShaping[i][rewardName] = value; } /** * Initialize default reward shaping for this scenario. Should be overloaded by all scenarios. */ virtual void initRewardShaping() { initRewards(defaultRewardShaping()); } /** * @param agentIdx * @return current reward shaping for the agent */ virtual RewardShaping & getRewardShaping(int agentIdx) { return rewardShaping[agentIdx]; } /** * @param agentIdx * @param rs new reward shaping for the agent */ virtual void setRewardShaping(int agentIdx, const RewardShaping &rs) { rewardShaping[agentIdx] = rs; } /** * Other utility functions. */ public: /** * Polymorphically initialize params, if derived scenarios have custom default parameters they should override * this method. */ virtual void initializeDefaultParameters() { auto &fp = floatParams; fp[Str::episodeLengthSec] = 60.0f; fp[Str::verticalLookLimitRad] = 0.2f; fp[Str::useUIRewardIndicators] = 0.0f; } virtual const FloatParams & getFloatParams() const { return floatParams; } /** * This default behavior should typically be sufficient, although can also be overridden. */ virtual void setCustomParameters(const FloatParams &customFloatParams) { for (const auto &[k, v] : customFloatParams) floatParams[k] = v; } protected: /** * @return reward for a specific string key for a particular agent in the environment * Since different agents can be controlled by different policies, they can also have different reward shaping * associated with them (e.g. when we're doing PBT). * Therefore we need a separate rewardShaping dictionary for every agent. */ virtual float getReward(const std::string &rewardName, int agentIdx) const { return rewardShaping[agentIdx].at(rewardName); } /** * Reward agent individually, do not reward other agents even if teamSpirit > 0 */ virtual void rewardAgent(const std::string &rewardName, int agentIdx, float multiplier) { envState.lastReward[agentIdx] += getReward(rewardName, agentIdx) * multiplier; } /** * @return teamSpirit configured for this agent, can be used in collaborative tasks. */ virtual float teamSpirit(int agentIdx) const { return getReward(Str::teamSpirit, agentIdx); } /** * Which team the agent belongs to. By default all agents are on the same team. */ virtual int teamAffinity(int /*agentIdx*/) const { return 0; } virtual int teamSize(const int agentIdx) const { const int currTeam = teamAffinity(agentIdx); int size = 0; for (int i = 0; i < env.getNumAgents(); ++i) size += currTeam == teamAffinity(i) ? 1 : 0; return size; } /** * Reward all agents, taking teamSpirit into account. Can be useful for collaborative tasks. * TeamSpirit is expected to be in [0, 1] range. * The agent whose action was rewarded gets the full reward. Other agents get teamSpirit * reward. */ virtual void rewardTeam(const std::string &rewardName, int agentIdx, float multiplier) { const auto currTeam = teamAffinity(agentIdx); rewardAgent(rewardName, agentIdx, multiplier * (1 - teamSpirit(agentIdx))); for (int i = 0; i < env.getNumAgents(); ++i) if (teamAffinity(i) == currTeam) envState.lastReward[i] += getReward(rewardName, i) * teamSpirit(i) * multiplier / teamSize(i); } /** * Reward all agents equally, regardless of team spirit. */ virtual void rewardAll(const std::string &rewardName, float multiplier) { for (int i = 0; i < env.getNumAgents(); ++i) rewardAgent(rewardName, i, multiplier); } private: static ScenarioRegistry & getScenarioRegistry() { static ScenarioRegistry scenarioRegistry; return scenarioRegistry; } protected: std::string scenarioName; // default values, can be overridden in ctor FloatParams floatParams; /** * Adds an optional mechanism for components to access each other. * Scenarios may decide to register or not to register their components here. */ std::vector components; Env &env; Env::EnvState &envState; // reward shaping schemes for every agent in the env std::vector rewardShaping; }; } ================================================ FILE: src/libs/env/include/env/scenario_component.hpp ================================================ #pragma once #include namespace Megaverse { /** * We decompose behaviors in scenarios into reusable components which can be used in multiple scenarios. * This is a base class for all scenario components. */ class ScenarioComponent { public: explicit ScenarioComponent(Scenario &scenario) : scenario{scenario} { } virtual ~ScenarioComponent() = default; public: virtual void reset(Env &, Env::EnvState &) {} virtual void step(Env &, Env::EnvState &) {} protected: /** * When needed a component can access the parent scenario. */ Scenario &scenario; }; } ================================================ FILE: src/libs/env/include/env/vector_env.hpp ================================================ #pragma once #include #include #include #include #include namespace Megaverse { class VectorEnv { public: enum class Task { IDLE, STEP, RESET, TERMINATE, }; public: explicit VectorEnv(Envs &envs, EnvRenderer &renderer, int numThreads); void step(); void reset(); void close(); private: void taskFunc(Task task, int threadIdx); void executeTask(Task task); void stepEnv(int envIdx); void resetEnv(int envIdx); public: std::vector> &envs; EnvRenderer &renderer; std::vector done; std::vector> trueObjectives; private: int numThreads{}, envsPerThread{}; std::vector backgroundThreads; std::vector currTasks; std::condition_variable cvTask; std::mutex mutex; std::atomic numReady = 0; }; } ================================================ FILE: src/libs/env/include/env/voxel_state.hpp ================================================ #pragma once #include #include namespace Megaverse { enum VoxelType { VOXEL_EMPTY = 0, // is not a part of the layout, but may contain an object, so there's still entry in the voxel grid VOXEL_SOLID = 0b1, // whether a voxel requires collision or not, i.e. a regular layout VOXEL_OPAQUE = 0b10, // whether a voxel needs to be drawn on screen, don't set this if you want an invisible wall }; struct VoxelState { VoxelState() : voxelType{VOXEL_EMPTY} , terrain{0} { } bool solid() const { return voxelType & VOXEL_SOLID; } bool empty() const { return !solid(); } bool opaque() const { return voxelType & VOXEL_OPAQUE; } static uint8_t generateType(bool solid, bool opaque) { return solid | (opaque << 1); } public: uint8_t voxelType{}, terrain{}; ColorRgb color{ColorRgb::LAYOUT_DEFAULT}; }; template auto makeVoxel(int type, int terrain = 0, ColorRgb color = ColorRgb::LAYOUT_DEFAULT) { VoxelT v; v.voxelType = type, v.terrain = terrain, v.color = color; return v; } } ================================================ FILE: src/libs/env/src/agent.cpp ================================================ #include #include #include #include #include #include using namespace Magnum; using namespace Magnum::Math::Literals; using namespace Megaverse; AbstractAgent::AbstractAgent(Object3D *parent, btDynamicsWorld &bWorld, float verticalLookLimitRad) : Object(parent) , verticalLookLimitRad{verticalLookLimitRad} , bWorld(bWorld) { } DefaultKinematicAgent::DefaultKinematicAgent(Object3D *parent, btDynamicsWorld &bWorld, const Vector3 &startingPosition, float rotationRad, float verticalLookLimitRad) : AbstractAgent(parent, bWorld, verticalLookLimitRad) , cameraObject{&(addChild())} , camera{&(cameraObject->addFeature())} , pickupSpot{&(cameraObject->addChild())} { // cameraObject.rotateY(0.0_degf); cameraObject->translate(Magnum::Vector3{0, 0.41f, 0}); auto [fov, near, far, aspectRatio] = agentCameraParameters(); camera->setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend) .setProjectionMatrix(Matrix4::perspectiveProjection(Deg(fov), aspectRatio, near, far)) .setViewport(GL::defaultFramebuffer.viewport().size()); pickupSpot->translate({0.0f, -0.44f, -1.0f}); btTransform startTransform; startTransform.setIdentity(); startTransform.setRotation(btQuaternion(btVector3(0, 1, 0), rotationRad)); startTransform.setOrigin (btVector3(startingPosition.x(), startingPosition.y() + getAgentHeight(), startingPosition.z())); ghostObject.setWorldTransform(startTransform); // btScalar characterHeight = 1.0f; //1.6 // btScalar characterRadius = 0.25f; // capsuleShape = std::make_unique(btVector3{characterRadius, agentHeight / 2, characterRadius}); btScalar characterHeight = 1.05f; //1.6 btScalar characterRadius = 0.33f; capsuleShape = std::make_unique(characterRadius, characterHeight); ghostObject.setCollisionShape(capsuleShape.get()); ghostObject.setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); auto stepHeight = btScalar(0.2f); bCharacter = std::make_unique(&ghostObject, capsuleShape.get(), stepHeight, btVector3(0.0, 1.0, 0.0)); // bWorld.addCollisionObject(&ghostObject, btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter); bWorld.addCollisionObject(&ghostObject, btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter); bWorld.addAction(bCharacter.get()); } DefaultKinematicAgent::~DefaultKinematicAgent() { bWorld.removeCollisionObject(&ghostObject); bWorld.removeAction(bCharacter.get()); } void DefaultKinematicAgent::updateTransform() { auto worldTrans = ghostObject.getWorldTransform(); auto position = Vector3{worldTrans.getOrigin()}; const auto axis = Vector3{worldTrans.getRotation().getAxis()}; const auto normalizedAxis = axis.normalized(); const Float rotation = worldTrans.getRotation().getAngle(); /* Bullet sometimes reports NaNs for all the parameters and nobody is sure why: https://pybullet.org/Bullet/phpBB3/viewtopic.php?t=12080. The body gets stuck in that state, so print the warning just once. */ if (Math::isNan(position).any() || Math::isNan(rotation)) { Error{} << "BulletIntegration::MotionState: Bullet reported NaN transform for" << this << Debug::nospace << ", ignoring"; return; } if (Math::isNan(normalizedAxis).any()) { Error{} << "BulletIntegration::MotionState: NaN normalized axis" << this << Debug::nospace << ", ignoring"; return; } position += Vector3{0, 0.05f, 0.0f}; this->resetTransformation().rotate(Rad{rotation}, normalizedAxis).translate(position); } void DefaultKinematicAgent::lookLeft(float dt) { rotateYAxis(rotateRadians * dt); } void DefaultKinematicAgent::lookRight(float dt) { rotateYAxis(-rotateRadians * dt); } void DefaultKinematicAgent::lookUp(float dt) { cameraObject->rotateXLocal(Math::Rad(-currXRotation)); currXRotation += rotateXRadians * dt; currXRotation = std::min(verticalLookLimitRad, currXRotation); cameraObject->rotateXLocal(Math::Rad(currXRotation)); } void DefaultKinematicAgent::lookDown(float dt) { cameraObject->rotateXLocal(Math::Rad(-currXRotation)); // this is a hack, in the beginning of training the agents really love to look at the sky and can't learn anything // by making looking down easier than up I hope to prevent this currXRotation -= rotateXRadians * dt * 1.1f; currXRotation = std::max(-verticalLookLimitRad, currXRotation); cameraObject->rotateXLocal(Math::Rad(currXRotation)); } void DefaultKinematicAgent::rotateYAxis(float radians) { btMatrix3x3 orn = ghostObject.getWorldTransform().getBasis(); orn *= btMatrix3x3(btQuaternion(btVector3(0, 1, 0), radians)); ghostObject.getWorldTransform ().setBasis(orn); } btVector3 DefaultKinematicAgent::forwardDirection() const { auto xform = ghostObject.getWorldTransform (); btVector3 forwardDir = xform.getBasis()[2]; forwardDir.setZ(-forwardDir.z()); // TLOG(INFO) << "forwardDir: " << forwardDir.x() << " " << forwardDir.y() << " " << forwardDir.z(); return forwardDir.normalize(); } btVector3 DefaultKinematicAgent::strafeLeftDirection() const { auto xform = ghostObject.getWorldTransform (); btVector3 sidewaysDir = xform.getBasis()[0]; sidewaysDir.setX(-sidewaysDir.x()); return sidewaysDir.normalize(); } void DefaultKinematicAgent::accelerate(const btVector3 &acc, btScalar frameDuration) { bCharacter->setAcceleration(acc, frameDuration); } void DefaultKinematicAgent::jump() { if (onGround()) bCharacter->jump(btVector3(0, 6.2, 0)); } void DefaultKinematicAgent::teleport(const btVector3 &position) { bCharacter->warp(position); } bool DefaultKinematicAgent::onGround() const { return bCharacter->onGround(); } ================================================ FILE: src/libs/env/src/env.cpp ================================================ #include #include #include #include #include using namespace Magnum; using namespace Magnum::Math::Literals; using namespace Megaverse; /** Left = 1 << 1, Right = 1 << 2, Forward = 1 << 3, Backward = 1 << 4, LookLeft = 1 << 5, LookRight = 1 << 6, Jump = 1 << 7, Interact = 1 << 8, LookDown = 1 << 9, LookUp = 1 << 10, **/ const std::vector Env::actionSpaceSizes = {3, 3, 3, 2, 2, 3}; Env::Env(const std::string &scenarioName, int numAgents, const FloatParams& customFloatParams) : scenarioName{scenarioName} , state{numAgents} , numAgents{numAgents} { scenario = Scenario::create(scenarioName, *this, state); scenario->init(); scenario->setCustomParameters(customFloatParams); // empty list of drawables for each supported drawable type for (int drawableType = int(DrawableType::First); drawableType < int(DrawableType::NumTypes); ++drawableType) drawables[DrawableType(drawableType)] = std::vector{}; } Env::~Env() = default; void Env::seed(int seedValue) { state.rng.seed((unsigned long)seedValue); } void Env::reset() { state.reset(); auto seed = randRange(0, 1 << 30, state.rng); state.rng.seed((unsigned long)seed); // TLOG(INFO) << "Using seed " << seed; // remove dangling pointers from the previous episode for (int drawableType = int(DrawableType::First); drawableType < int(DrawableType::NumTypes); ++drawableType) drawables[DrawableType(drawableType)].clear(); scenario->reset(); scenario->spawnAgents(state.agents); scenario->addEpisodeDrawables(drawables); scenario->addEpisodeAgentsDrawables(drawables); scenario->addUIDrawables(drawables); } void Env::setAction(int agentIdx, Action action) { state.currAction[agentIdx] = action; } void Env::step() { std::fill(state.lastReward.begin(), state.lastReward.end(), 0.0f); const auto lastFrameDurationSec = state.lastFrameDurationSec; for (int i = 0; i < numAgents; ++i) { const auto a = state.currAction[i]; const auto &agent = state.agents[i]; auto acceleration = btVector3{0, 0, 0}; if (!!(a & Action::Forward)) acceleration += agent->forwardDirection(); else if (!!(a & Action::Backward)) acceleration -= agent->forwardDirection(); if (!!(a & Action::Left)) acceleration += agent->strafeLeftDirection(); else if (!!(a & Action::Right)) acceleration -= agent->strafeLeftDirection(); if (!!(a & Action::LookLeft)) agent->lookLeft(lastFrameDurationSec); else if (!!(a & Action::LookRight)) agent->lookRight(lastFrameDurationSec); if (!!(a & Action::LookUp)) agent->lookUp(lastFrameDurationSec); else if (!!(a & Action::LookDown)) agent->lookDown(lastFrameDurationSec); // if (acceleration.length() > 0) // TLOG(INFO) << "acc direction: " << acceleration.x() << " " << acceleration.y() << " " << acceleration.z(); agent->accelerate(acceleration, lastFrameDurationSec); if (!!(a & Action::Jump)) agent->jump(); } scenario->preStep(); state.physics->bWorld.stepSimulation(lastFrameDurationSec, 1, state.simulationStepSeconds); for (auto agent : state.agents) agent->updateTransform(); scenario->step(); state.currEpisodeSec += state.lastFrameDurationSec; scenario->updateUI(); if (state.currEpisodeSec >= episodeLengthSec()) state.done = true; // clear the actions for (int i = 0; i < numAgents; ++i) state.currAction[i] = Action::Idle; for (int i = 0; i < int(state.agents.size()); ++i) { state.totalReward[i] += state.lastReward[i]; // if (fabs(state.lastReward[i]) > SIMD_EPSILON) // TLOG(INFO) << "Last reward for agent #" << i << ": " << state.lastReward[i] << ", total reward: " << state.totalReward[i]; } ++state.numFrames; } std::vector Env::getPalette() const { return scenario->getPalette(); } float Env::episodeLengthSec() const { return scenario->episodeLengthSec(); } float Env::trueObjective(int agentIdx) const { return scenario->trueObjective(agentIdx); } void Env::terminateEpisodeOnNextFrame() { scenario->doneWithTimer(0.001f); } ================================================ FILE: src/libs/env/src/kinematic_character_controller.cpp ================================================ #include #include "LinearMath/btIDebugDraw.h" #include "BulletCollision/CollisionDispatch/btGhostObject.h" #include "BulletCollision/CollisionShapes/btMultiSphereShape.h" #include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" #include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" #include "BulletCollision/CollisionDispatch/btCollisionWorld.h" #include "LinearMath/btDefaultMotionState.h" #include #include using namespace Megaverse; #ifdef UNUSED_FUNCTION static btVector3 getNormalizedVector(const btVector3& v) { btVector3 n(0, 0, 0); if (v.length() > SIMD_EPSILON) n = v.normalized(); return n; } #endif ///@todo Interact with dynamic objects, ///Ride kinematicly animated platforms properly ///More realistic (or maybe just a config option) falling /// -> Should integrate falling velocity manually and use that in stepDown() ///Support jumping ///Support ducking class KinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback { public: KinematicClosestNotMeRayResultCallback(btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) { m_me = me; } virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) { if (rayResult.m_collisionObject == m_me) return 1.0; return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); } protected: btCollisionObject* m_me; }; class KinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { public: KinematicClosestNotMeConvexResultCallback(btCollisionObject* me, const btVector3& up, btScalar minSlopeDot) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), m_me(me), m_up(up), m_minSlopeDot(minSlopeDot) { } virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace) { if (convexResult.m_hitCollisionObject == m_me) return btScalar(1.0); if (!convexResult.m_hitCollisionObject->hasContactResponse()) return btScalar(1.0); btVector3 hitNormalWorld; if (normalInWorldSpace) { hitNormalWorld = convexResult.m_hitNormalLocal; } else { ///need to transform normal into worldspace hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis() * convexResult.m_hitNormalLocal; } btScalar dotUp = m_up.dot(hitNormalWorld); if (dotUp < m_minSlopeDot) { return btScalar(1.0); } return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); } protected: btCollisionObject* m_me; const btVector3 m_up; btScalar m_minSlopeDot; }; /* * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal' * * from: http://www-cs-students.stanford.edu/~adityagp/final/node3.html */ btVector3 KinematicCharacterController::computeReflectionDirection(const btVector3& direction, const btVector3& normal) { return direction - (btScalar(2.0) * direction.dot(normal)) * normal; } /* * Returns the portion of 'direction' that is parallel to 'normal' */ btVector3 KinematicCharacterController::parallelComponent(const btVector3& direction, const btVector3& normal) { btScalar magnitude = direction.dot(normal); return normal * magnitude; } /* * Returns the portion of 'direction' that is perpindicular to 'normal' */ btVector3 KinematicCharacterController::perpindicularComponent(const btVector3& direction, const btVector3& normal) { return direction - parallelComponent(direction, normal); } KinematicCharacterController::KinematicCharacterController(btPairCachingGhostObject* ghostObject, btConvexShape* convexShape, btScalar stepHeight, const btVector3& up) { m_ghostObject = ghostObject; m_up.setValue(0.0f, 1.0f, 0.0f); m_jumpAxis.setValue(0.0f, 1.0f, 0.0f); horizontalVelocity.setValue(0.0, 0.0, 0.0); m_AngVel.setValue(0.0, 0.0, 0.0); m_useGhostObjectSweepTest = true; m_turnAngle = btScalar(0.0); m_convexShape = convexShape; m_verticalVelocity = 0.0; m_verticalOffset = 0.0; m_fallSpeed = 55.0; // Terminal velocity of a sky diver in m/s. m_jumpSpeed = 10.0; // ? m_SetjumpSpeed = m_jumpSpeed; m_wasOnGround = false; m_wasJumping = false; m_interpolateUp = true; m_currentStepOffset = 0.0; m_angularDamping = btScalar(0.0); setUp(up); setStepHeight(stepHeight); setMaxSlope(btRadians(45.0)); } KinematicCharacterController::~KinematicCharacterController() = default; btPairCachingGhostObject* KinematicCharacterController::getGhostObject() { return m_ghostObject; } bool KinematicCharacterController::recoverFromPenetration(btCollisionWorld* collisionWorld, int /*iteration*/) { // Here we must refresh the overlapping paircache as the penetrating movement itself or the // previous recovery iteration might have used setWorldTransform and pushed us into an object // that is not in the previous cache contents from the last timestep, as will happen if we // are pushed into a new AABB overlap. Unhandled this means the next convex sweep gets stuck. // // Do this by calling the broadphase's setAabb with the moved AABB, this will update the broadphase // paircache and the ghostobject's internal paircache at the same time. /BW btVector3 minAabb, maxAabb; m_convexShape->getAabb(m_ghostObject->getWorldTransform(), minAabb, maxAabb); collisionWorld->getBroadphase()->setAabb(m_ghostObject->getBroadphaseHandle(), minAabb, maxAabb, collisionWorld->getDispatcher()); bool penetration = false; collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher()); m_currentPosition = m_ghostObject->getWorldTransform().getOrigin(); const auto overlappingPairCache = m_ghostObject->getOverlappingPairCache(); const auto numOverlappingPairs = overlappingPairCache->getNumOverlappingPairs(); for (int i = 0; i < numOverlappingPairs && !penetration; ++i) { m_manifoldArray.resize(0); btBroadphasePair* collisionPair = &overlappingPairCache->getOverlappingPairArray()[i]; auto * obj0 = static_cast(collisionPair->m_pProxy0->m_clientObject); auto * obj1 = static_cast(collisionPair->m_pProxy1->m_clientObject); if ((obj0 && !obj0->hasContactResponse()) || (obj1 && !obj1->hasContactResponse())) continue; if (!needsCollision(obj0, obj1)) continue; if (collisionPair->m_algorithm) collisionPair->m_algorithm->getAllContactManifolds(m_manifoldArray); for (int j = 0; j < m_manifoldArray.size() && !penetration; j++) { btPersistentManifold* manifold = m_manifoldArray[j]; btScalar directionSign = manifold->getBody0() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0); for (int p = 0; p < manifold->getNumContacts(); p++) { const btManifoldPoint& pt = manifold->getContactPoint(p); btScalar dist = pt.getDistance(); if (dist < -m_maxPenetrationDepth) { auto posDelta = pt.m_normalWorldOnB * directionSign * dist; m_currentPosition += posDelta; penetration = true; break; } } } } btTransform newTrans = m_ghostObject->getWorldTransform(); newTrans.setOrigin(m_currentPosition); m_ghostObject->setWorldTransform(newTrans); // printf("m_touchingNormal = %f,%f,%f\n",m_touchingNormal[0],m_touchingNormal[1],m_touchingNormal[2]); return penetration; } void KinematicCharacterController::stepUp(btCollisionWorld* world) { btScalar stepHeight = 0.0f; if (m_verticalVelocity < 0.0) stepHeight = m_stepHeight; // phase 1: up btTransform start, end; start.setIdentity(); end.setIdentity(); /* FIXME: Handle penetration properly */ start.setOrigin(m_currentPosition); m_targetPosition = m_currentPosition + m_up * (stepHeight) + m_jumpAxis * ((m_verticalOffset > 0.f ? m_verticalOffset : 0.f)); m_currentPosition = m_targetPosition; end.setOrigin(m_targetPosition); start.setRotation(m_currentOrientation); end.setRotation(m_targetOrientation); KinematicClosestNotMeConvexResultCallback callback(m_ghostObject, -m_up, m_maxSlopeCosine); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; if (m_useGhostObjectSweepTest) { m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration); } else { world->convexSweepTest(m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration); } if (callback.hasHit() && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback.m_hitCollisionObject)) { // Only modify the position if the hit was a slope and not a wall or ceiling. if (callback.m_hitNormalWorld.dot(m_up) > 0.0) { // we moved up only a fraction of the step height m_currentStepOffset = stepHeight * callback.m_closestHitFraction; if (m_interpolateUp == true) m_currentPosition.setInterpolate3(m_currentPosition, m_targetPosition, callback.m_closestHitFraction); else m_currentPosition = m_targetPosition; } btTransform& xform = m_ghostObject->getWorldTransform(); xform.setOrigin(m_currentPosition); m_ghostObject->setWorldTransform(xform); // fix penetration if we hit a ceiling for example int numPenetrationLoops = 0; m_touchingContact = false; while (recoverFromPenetration(world, numPenetrationLoops)) { numPenetrationLoops++; m_touchingContact = true; if (numPenetrationLoops > 4) { //printf("character could not recover from penetration = %d\n", numPenetrationLoops); break; } } m_targetPosition = m_ghostObject->getWorldTransform().getOrigin(); m_currentPosition = m_targetPosition; if (m_verticalOffset > 0) { m_verticalOffset = 0.0; m_verticalVelocity = 0.0; m_currentStepOffset = m_stepHeight; } } else { m_currentStepOffset = stepHeight; m_currentPosition = m_targetPosition; } } bool KinematicCharacterController::needsCollision(const btCollisionObject* body0, const btCollisionObject* body1) { bool collides = (body0->getBroadphaseHandle()->m_collisionFilterGroup & body1->getBroadphaseHandle()->m_collisionFilterMask) != 0; collides = collides && (body1->getBroadphaseHandle()->m_collisionFilterGroup & body0->getBroadphaseHandle()->m_collisionFilterMask); return collides; } void KinematicCharacterController::updateTargetPositionBasedOnCollision(const btVector3& hitNormal, btScalar fraction) { btVector3 movementDirection = m_targetPosition - m_currentPosition; btScalar movementLength = movementDirection.length(); if (movementLength > SIMD_EPSILON) { movementDirection.normalize(); auto parallelDir = parallelComponent(movementDirection, hitNormal); auto perpindicularDir = perpindicularComponent(movementDirection, hitNormal); m_targetPosition = m_currentPosition; // arrest the momentum in the direction of the normal, continue movement in the perpendicular direction m_targetPosition += perpindicularDir * btScalar(movementLength); m_targetPosition += parallelDir * btScalar(movementLength * fraction); } } /** * Phase 2: react to directional user input * @param collisionWorld * @param horizontalVelocity * @param dt */ void KinematicCharacterController::stepForwardAndStrafe(btCollisionWorld* collisionWorld, const btVector3& horizontalVelocity, btScalar dt) { m_targetPosition = m_currentPosition + horizontalVelocity * dt; btTransform start, end; start.setIdentity(); end.setIdentity(); int maxIter = 10, iteration = 0; while (maxIter-- > 0) { ++iteration; start.setOrigin(m_currentPosition); end.setOrigin(m_targetPosition); btVector3 sweepDirNegative(m_currentPosition - m_targetPosition); start.setRotation(m_currentOrientation); end.setRotation(m_targetOrientation); KinematicClosestNotMeConvexResultCallback callback(m_ghostObject, sweepDirNegative, btScalar(0.0)); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; if (!(start == end)) { // desired movement is > 0 m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); } if (callback.hasHit() && m_ghostObject->hasContactResponse() && needsCollision(m_ghostObject, callback.m_hitCollisionObject)) { // we moved only a fraction updateTargetPositionBasedOnCollision(callback.m_hitNormalWorld, callback.m_closestHitFraction); btVector3 currentDir = m_targetPosition - m_currentPosition; auto distance2 = currentDir.length2(); if (distance2 > 0.0001f) { currentDir.normalize(); /* See Quake2: "If velocity is against original velocity, stop dead to avoid tiny oscilations in sloping corners." */ if (currentDir.dot(horizontalVelocity) <= btScalar(0.0)) { // cancel the movement m_targetPosition = m_currentPosition; break; } } else { // cancel the movement m_targetPosition = m_currentPosition; break; } } else { break; } } m_currentPosition = m_targetPosition; } /** phase 3: down * @param collisionWorld * @param dt */ void KinematicCharacterController::stepDown(btCollisionWorld *collisionWorld, btScalar dt) { btScalar downVelocity = (m_verticalVelocity < 0.f ? -m_verticalVelocity : 0.f); // limit fall terminal velocity?? if (downVelocity > 0.0 && downVelocity > m_fallSpeed && (m_wasOnGround || !m_wasJumping)) downVelocity = m_fallSpeed; btVector3 step_drop = m_up * (m_currentStepOffset + downVelocity * dt); m_targetPosition -= step_drop; KinematicClosestNotMeConvexResultCallback callback(m_ghostObject, m_up, m_maxSlopeCosine); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; { btTransform start, end; start.setIdentity(); end.setIdentity(); start.setOrigin(m_currentPosition); end.setOrigin(m_targetPosition); start.setRotation(m_currentOrientation); end.setRotation(m_targetOrientation); m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); } if ((m_ghostObject->hasContactResponse() && (callback.hasHit() && needsCollision(m_ghostObject, callback.m_hitCollisionObject)))) { // we dropped a fraction of the height and hit the floor // because of allowed penetration this will move us more downwards than we need to (we penetrate the floor polygon) // this should be handled later by recoverFromPenetration? m_currentPosition.setInterpolate3(m_currentPosition, m_targetPosition, callback.m_closestHitFraction); m_verticalVelocity = 0.0; m_verticalOffset = 0.0; m_wasJumping = false; } else { // we dropped the full height, didn't hit anything m_currentPosition = m_targetPosition; } } void KinematicCharacterController::setWalkDirection( const btVector3& walkDirection) { horizontalVelocity = walkDirection; } void KinematicCharacterController::setAngularVelocity(const btVector3& velocity) { m_AngVel = velocity; } const btVector3& KinematicCharacterController::getAngularVelocity() const { return m_AngVel; } void KinematicCharacterController::setLinearVelocity(const btVector3& velocity) { horizontalVelocity = velocity; // HACK: if we are moving in the direction of the up, treat it as a jump :( if (horizontalVelocity.length2() > 0) { btVector3 w = velocity.normalized(); btScalar c = w.dot(m_up); if (c != 0) { //there is a component in walkdirection for vertical velocity btVector3 upComponent = m_up * (btSin(SIMD_HALF_PI - btAcos(c)) * horizontalVelocity.length()); horizontalVelocity -= upComponent; m_verticalVelocity = (c < 0.0f ? -1 : 1) * upComponent.length(); if (c > 0.0f) { m_wasJumping = true; m_jumpPosition = m_ghostObject->getWorldTransform().getOrigin(); } } } else m_verticalVelocity = 0.0f; } btVector3 KinematicCharacterController::getLinearVelocity() const { return horizontalVelocity + (m_verticalVelocity * m_up); } void KinematicCharacterController::reset(btCollisionWorld* collisionWorld) { m_verticalVelocity = 0.0; m_verticalOffset = 0.0; m_wasOnGround = false; m_wasJumping = false; horizontalVelocity.setValue(0, 0, 0); //clear pair cache btHashedOverlappingPairCache* cache = m_ghostObject->getOverlappingPairCache(); while (cache->getOverlappingPairArray().size() > 0) { cache->removeOverlappingPair(cache->getOverlappingPairArray()[0].m_pProxy0, cache->getOverlappingPairArray()[0].m_pProxy1, collisionWorld->getDispatcher()); } } void KinematicCharacterController::warp(const btVector3& origin) { btTransform xform; xform.setIdentity(); xform.setOrigin(origin); m_ghostObject->setWorldTransform(xform); horizontalVelocity.setValue(0, 0, 0); m_verticalVelocity = 0; } void KinematicCharacterController::preStep(btCollisionWorld* /*collisionWorld*/) { m_currentPosition = m_ghostObject->getWorldTransform().getOrigin(); m_targetPosition = m_currentPosition; m_currentOrientation = m_ghostObject->getWorldTransform().getRotation(); m_targetOrientation = m_currentOrientation; } void KinematicCharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar dt) { const auto originalPosition = m_currentPosition; if (m_AngVel.length2() > 0.0f) m_AngVel *= btPow(btScalar(1) - m_angularDamping, dt); // integrate for angular velocity if (m_AngVel.length2() > 0.0f) { btTransform xform = m_ghostObject->getWorldTransform(); btQuaternion rot(m_AngVel.normalized(), m_AngVel.length() * dt); btQuaternion orn = rot * xform.getRotation(); xform.setRotation(orn); m_ghostObject->setWorldTransform(xform); m_currentPosition = m_ghostObject->getWorldTransform().getOrigin(); m_targetPosition = m_currentPosition; m_currentOrientation = m_ghostObject->getWorldTransform().getRotation(); m_targetOrientation = m_currentOrientation; } m_wasOnGround = onGround(); // Update fall velocity. m_verticalVelocity -= m_gravity * dt; if (m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed) m_verticalVelocity = m_jumpSpeed; if (m_verticalVelocity < 0.0 && btFabs(m_verticalVelocity) > btFabs(m_fallSpeed)) m_verticalVelocity = -btFabs(m_fallSpeed); m_verticalOffset = m_verticalVelocity * dt; btTransform xform; xform = m_ghostObject->getWorldTransform(); stepUp(collisionWorld); stepForwardAndStrafe(collisionWorld, horizontalVelocity, dt); stepDown(collisionWorld, dt); xform.setOrigin(m_currentPosition); m_ghostObject->setWorldTransform(xform); // how far we actually traveled. If we hit a wall this will arrest the momentum horizontalVelocity = (m_currentPosition - originalPosition) / dt; horizontalVelocity.setY(0); // we only care about velocity in horizontal plane int numPenetrationLoops = 0; m_touchingContact = false; while (recoverFromPenetration(collisionWorld, numPenetrationLoops)) { numPenetrationLoops++; m_touchingContact = true; if (numPenetrationLoops > 4) break; } auto currHorizontalSpeed = horizontalVelocity.length(); if (onGround()) { // friction if (currHorizontalSpeed - normalDeceleration * dt < 0) horizontalVelocity.setZero(); else horizontalVelocity *= (currHorizontalSpeed - normalDeceleration * dt) / currHorizontalSpeed; } // TLOG(INFO) << "Horizontal speed: " << horizontalVelocity.length(); } void KinematicCharacterController::setFallSpeed(btScalar fallSpeed) { m_fallSpeed = fallSpeed; } void KinematicCharacterController::setJumpSpeed(btScalar jumpSpeed) { m_jumpSpeed = jumpSpeed; m_SetjumpSpeed = m_jumpSpeed; } void KinematicCharacterController::setMaxJumpHeight(btScalar maxJumpHeight) { m_maxJumpHeight = maxJumpHeight; } bool KinematicCharacterController::canJump() const { return onGround(); } void KinematicCharacterController::jump(const btVector3& v) { m_jumpSpeed = v.length2() == 0 ? m_SetjumpSpeed : v.length(); m_verticalVelocity = m_jumpSpeed; m_wasJumping = true; m_jumpAxis = v.length2() == 0 ? m_up : v.normalized(); m_jumpPosition = m_ghostObject->getWorldTransform().getOrigin(); #if 0 currently no jumping. btTransform xform; m_rigidBody->getMotionState()->getWorldTransform (xform); btVector3 up = xform.getBasis()[1]; up.normalize (); btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0); m_rigidBody->applyCentralImpulse (up * magnitude); #endif } void KinematicCharacterController::setGravity(const btVector3& gravity) { if (gravity.length2() > 0) setUpVector(-gravity); m_gravity = gravity.length(); } btVector3 KinematicCharacterController::getGravity() const { return -m_gravity * m_up; } void KinematicCharacterController::setMaxSlope(btScalar slopeRadians) { m_maxSlopeRadians = slopeRadians; m_maxSlopeCosine = btCos(slopeRadians); } btScalar KinematicCharacterController::getMaxSlope() const { return m_maxSlopeRadians; } void KinematicCharacterController::setMaxPenetrationDepth(btScalar d) { m_maxPenetrationDepth = d; } btScalar KinematicCharacterController::getMaxPenetrationDepth() const { return m_maxPenetrationDepth; } bool KinematicCharacterController::onGround() const { return (fabs(m_verticalVelocity) < SIMD_EPSILON) && (fabs(m_verticalOffset) < SIMD_EPSILON); } void KinematicCharacterController::setStepHeight(btScalar h) { m_stepHeight = h; } btVector3* KinematicCharacterController::getUpAxisDirections() { static btVector3 sUpAxisDirection[3] = {btVector3(1.0f, 0.0f, 0.0f), btVector3(0.0f, 1.0f, 0.0f), btVector3(0.0f, 0.0f, 1.0f)}; return sUpAxisDirection; } void KinematicCharacterController::debugDraw(btIDebugDraw* /*debugDrawer*/) { } void KinematicCharacterController::setUpInterpolate(bool value) { m_interpolateUp = value; } void KinematicCharacterController::setUp(const btVector3& up) { if (up.length2() > 0 && m_gravity > 0.0f) { setGravity(-m_gravity * up.normalized()); return; } setUpVector(up); } void KinematicCharacterController::setUpVector(const btVector3& up) { if (m_up == up) return; btVector3 u = m_up; if (up.length2() > 0) m_up = up.normalized(); else m_up = btVector3(0.0, 0.0, 0.0); if (!m_ghostObject) return; btQuaternion rot = getRotation(m_up, u); //set orientation with new up btTransform xform; xform = m_ghostObject->getWorldTransform(); btQuaternion orn = rot.inverse() * xform.getRotation(); xform.setRotation(orn); m_ghostObject->setWorldTransform(xform); } btQuaternion KinematicCharacterController::getRotation(btVector3& v0, btVector3& v1) const { if (v0.length2() == 0.0f || v1.length2() == 0.0f) { btQuaternion q; return q; } return shortestArcQuatNormalize2(v0, v1); } /** * @param acc acceleration vector * @param dt time passed since last frame in seconds */ void KinematicCharacterController::setAcceleration(btVector3 acc, btScalar dt) { const bool isOnGround = onGround(); const auto accelerationMagnitude = acc.length(); const auto currMaxAcceleration = isOnGround ? maxAcceleration : maxAirAcceleration; // we always apply max possible acceleration in the desired direction if (!acc.fuzzyZero()) acc *= currMaxAcceleration / accelerationMagnitude; if (isOnGround) { // apply acceleration to change velocity horizontalVelocity += acc * dt; const auto currHorizontalSpeed = horizontalVelocity.length(); // check if we exceed the max speed, decelerate if necessary if (currHorizontalSpeed > maxHorizontalSpeed) { const auto dv = exceedingSpeedLimitDeceleration * dt; if (currHorizontalSpeed - dv > maxHorizontalSpeed) { // after applying large deceleration we're still traveling too fast - just subtract the deceleration horizontalVelocity *= (currHorizontalSpeed - dv) / currHorizontalSpeed; } else { // if we just subtract deceleration we'd be traveling too slow // decelerate to max walking speed horizontalVelocity *= maxHorizontalSpeed / currHorizontalSpeed; } } } else { // in the air // there is no friction or deceleration const auto currHorizontalSpeed = horizontalVelocity.length(); const auto newHorizontalVelocity = horizontalVelocity + acc * dt; const auto newHorizontalSpeed = newHorizontalVelocity.length(); if (newHorizontalSpeed <= maxAirSpeed || newHorizontalSpeed < currHorizontalSpeed) horizontalVelocity = newHorizontalVelocity; } } ================================================ FILE: src/libs/env/src/vector_env.cpp ================================================ #include using namespace Megaverse; VectorEnv::VectorEnv(std::vector> &envs, EnvRenderer &renderer, int numThreads) : envs(envs) , renderer(renderer) , numThreads{numThreads} // use master threads as one of the threads { const int numEnvs = int(envs.size()); envsPerThread = (numEnvs / numThreads) + (numEnvs % numThreads != 0); currTasks = std::vector(size_t(numThreads), Task::IDLE); for (int i = 1; i < numThreads; ++i) { std::thread t{ [this](int threadIdx) { while (true) { std::unique_lock lock{mutex}; while (currTasks[threadIdx] == Task::IDLE) cvTask.wait(lock); const auto task = currTasks[threadIdx]; currTasks[threadIdx] = Task::IDLE; lock.unlock(); taskFunc(task, threadIdx); ++numReady; if (task == Task::TERMINATE) break; } }, i }; backgroundThreads.emplace_back(std::move(t)); } done = std::vector(envs.size()); trueObjectives = std::vector>(envs.size()); for (int envIdx = 0; envIdx < numEnvs; ++envIdx) trueObjectives[envIdx] = std::vector(envs[envIdx]->getNumAgents()); } void VectorEnv::stepEnv(int envIdx) { envs[envIdx]->step(); renderer.preDraw(*envs[envIdx], envIdx); } void VectorEnv::resetEnv(int envIdx) { envs[envIdx]->reset(); } void VectorEnv::taskFunc(Task task, int threadIdx) { auto func = &VectorEnv::stepEnv; if (task == Task::RESET) func = &VectorEnv::resetEnv; const auto startIdx = threadIdx * envsPerThread; const auto endIdx = std::min(startIdx + envsPerThread, int(envs.size())); for (int envIdx = startIdx; envIdx < endIdx; ++envIdx) (this->*func)(envIdx); } void VectorEnv::executeTask(Task task) { numReady = 0; std::unique_lock lock{mutex}; for (int threadIdx = 1; threadIdx < numThreads; ++threadIdx) currTasks[threadIdx] = task; cvTask.notify_all(); lock.unlock(); taskFunc(task, 0); // just waiting on an atomic, this is a bit faster than conditional variable // tradeoff - using more CPU cycles? while (numReady < numThreads - 1); } void VectorEnv::step() { executeTask(Task::STEP); // do this in background thread?? for (int envIdx = 0; envIdx < int(envs.size()); ++envIdx) { if (envs[envIdx]->isDone()) { done[envIdx] = true; for (int agentIdx = 0; agentIdx < envs[envIdx]->getNumAgents(); ++agentIdx) trueObjectives[envIdx][agentIdx] = envs[envIdx]->trueObjective(agentIdx); envs[envIdx]->reset(); renderer.reset(*envs[envIdx], envIdx); renderer.preDraw(*envs[envIdx], envIdx); } else { done[envIdx] = false; } } renderer.draw(envs); } void VectorEnv::reset() { // reset renderer on the main thread for (int envIdx = 0; envIdx < int(envs.size()); ++envIdx) { envs[envIdx]->reset(); renderer.reset(*envs[envIdx], envIdx); renderer.preDraw(*envs[envIdx], envIdx); } renderer.draw(envs); } void VectorEnv::close() { executeTask(Task::TERMINATE); for (auto &t : backgroundThreads) t.join(); } ================================================ FILE: src/libs/magnum_rendering/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.10) project(libgfx-magnum VERSION 0.1 LANGUAGES CXX) add_library_default(magnum_rendering) target_link_libraries(magnum_rendering PUBLIC env rendering Corrade::Main Magnum::GL Magnum::Shaders Magnum::MeshTools Magnum::Primitives MagnumIntegration::Bullet) find_package (Corrade REQUIRED Main) if (NOT CORRADE_TARGET_APPLE) find_package(Magnum REQUIRED WindowlessEglApplication) target_link_libraries(magnum_rendering PUBLIC glad Magnum::WindowlessEglApplication) else () find_package(Magnum REQUIRED WindowlessCglApplication) target_link_libraries(magnum_rendering PUBLIC Magnum::WindowlessCglApplication) endif () ================================================ FILE: src/libs/magnum_rendering/include/magnum_rendering/magnum_env_renderer.hpp ================================================ #pragma once #include #include #include #include #include #include #include namespace Megaverse { class MagnumEnvRenderer : public EnvRenderer { public: explicit MagnumEnvRenderer( Envs &envs, int w, int h, bool withDebugDraw = false, bool withOverview = false, RenderingContext *ctx = nullptr ); ~MagnumEnvRenderer() override; void reset(Env &env, int envIdx) override; void preDraw(Env &env, int envIdx) override; void draw(Envs &envs) override; void drawAgent(Env &env, int envIdx, int agentIndex, bool readToBuffer); const uint8_t * getObservation(int envIdx, int agentIdx) const override; Magnum::GL::Framebuffer *getFramebuffer(); void toggleDebugMode(); Overview * getOverview() override; private: struct Impl; std::unique_ptr pimpl; }; } ================================================ FILE: src/libs/magnum_rendering/include/magnum_rendering/rendering_context.hpp ================================================ #pragma once #include namespace Megaverse { class RenderingContext { public: virtual ~RenderingContext() = default; virtual void makeCurrent() = 0; }; class WindowRenderingContext : public RenderingContext { public: void makeCurrent() override { // noop, this is only for test apps that never switch context } }; class WindowlessContext : public RenderingContext { public: explicit WindowlessContext(int gpuDevice = 0); ~WindowlessContext() override; void makeCurrent() override; protected: struct Impl; std::unique_ptr pimpl; }; } ================================================ FILE: src/libs/magnum_rendering/src/magnum_env_renderer.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Magnum; using namespace Magnum::Math::Literals; using namespace Megaverse; struct InstanceData { Magnum::Matrix4 transformationMatrix; Magnum::Matrix3x3 normalMatrix; Magnum::Color3 color; }; class CustomDrawable : public SceneGraph::Drawable3D { public: explicit CustomDrawable( SceneGraph::AbstractObject3D &parentObject, Containers::Array &instanceData, const Color3 &color, SceneGraph::DrawableGroup3D &drawables ) : SceneGraph::Drawable3D{parentObject, &drawables}, _instanceData(instanceData), _color{color} {} private: void draw(const Matrix4& t, SceneGraph::Camera3D&) override { arrayAppend(_instanceData, Containers::InPlaceInit, t, t.normalMatrix(), _color); } Containers::Array& _instanceData; Color3 _color; }; #ifdef UNUSED class SimpleDrawable3D : public Object3D, public SceneGraph::Drawable3D { public: explicit SimpleDrawable3D(SceneGraph::DrawableGroup3D &drawables, Shaders::Phong & shader, GL::Mesh& mesh, const Color3 &color, Object3D *parentObject) : Object3D{parentObject} , SceneGraph::Drawable3D{*this, &drawables} , _shader(shader), _mesh(mesh), _color(color) {} void draw(const Matrix4& transformationMatrix, SceneGraph::Camera3D& camera) override { _shader.setTransformationMatrix(transformationMatrix) .setNormalMatrix(transformationMatrix.normalMatrix()) .setProjectionMatrix(camera.projectionMatrix()) .setAmbientColor(0.4 * _color) .setDiffuseColor(_color) .setShininess(150) /* relative to the camera */ .setLightPosition({13.0f, 2.0f, 5.0f}) .setLightColor(0xaaaaaa_rgbf) .draw(_mesh); } private: Shaders::Phong& _shader; GL::Mesh& _mesh; Color3 _color; }; #endif struct MagnumEnvRenderer::Impl { public: explicit Impl(Envs &envs, int w, int h, bool withDebugDraw = false, bool withOverview = false, RenderingContext *ctx = nullptr); ~Impl(); RenderingContext * initContext(RenderingContext *ctx); /** * Reset the state of the renderer between episodes. * @param env */ void reset(Env &env, int envIndex); void preDraw(Env &env, int envIndex); void draw(Envs &envs); void drawAgent(Env &env, int envIndex, int agentIdx, bool readToBuffer); uint8_t * getObservation(int envIdx, int agentIdx); GL::Framebuffer * getFramebuffer() { return &framebuffer; } void toggleDebugMode() { withDebugDraw = !withDebugDraw; } Overview * getOverview() { return &overview; } public: std::unique_ptr windowlessContextPtr{nullptr}; RenderingContext *ctx = nullptr; Vector2i framebufferSize; std::vector envDrawables; std::map instanceBuffers; std::map> instanceData; Shaders::Phong shader{NoCreate}; Shaders::Phong shaderInstanced{NoCreate}; GL::Framebuffer framebuffer; GL::Renderbuffer colorBuffer, depthBuffer; std::map meshData; std::map meshes; std::vector>> agentFrames; std::vector>> agentImageViews; bool withDebugDraw = false; BulletIntegration::DebugDraw debugDraw{NoCreate}; bool withOverviewCamera = false; Overview overview; }; MagnumEnvRenderer::Impl::Impl(Envs &envs, int w, int h, bool withDebugDraw, bool withOverview, RenderingContext *ctx) : ctx{initContext(ctx)} , framebufferSize{w, h} , framebuffer{Magnum::Range2Di{{}, framebufferSize}} , withDebugDraw{withDebugDraw} , withOverviewCamera{withOverview} { assert(!envs.empty()); MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GL330); GL::Renderer::enable(GL::Renderer::Feature::DepthTest); GL::Renderer::enable(GL::Renderer::Feature::FaceCulling); colorBuffer.setStorage(GL::RenderbufferFormat::SRGB8Alpha8, framebufferSize); depthBuffer.setStorage(GL::RenderbufferFormat::DepthComponent24, framebufferSize); framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, colorBuffer); framebuffer.attachRenderbuffer(GL::Framebuffer::BufferAttachment::Depth, depthBuffer); framebuffer.mapForDraw({{Shaders::Phong::ColorOutput, GL::Framebuffer::ColorAttachment{0}}}); framebuffer.clearColor(0, Color3{0.125f}).clearDepth(1.0).bind(); CORRADE_INTERNAL_ASSERT(framebuffer.checkStatus(GL::FramebufferTarget::Draw) == GL::Framebuffer::Status::Complete); TLOG(INFO) << "Creating Magnum env renderer " << w << " " << h << " " << envs.size(); for (const auto &e : envs) { std::vector> envAgentFrames; std::vector> envAgentImageViews; for (int i = 0; i < e->getNumAgents(); ++i) { envAgentFrames.emplace_back(size_t(framebufferSize.x() * framebufferSize.y() * 4)); envAgentImageViews.emplace_back( std::make_unique(PixelFormat::RGBA8Unorm, framebufferSize, envAgentFrames[i]) ); } agentFrames.emplace_back(std::move(envAgentFrames)); agentImageViews.emplace_back(std::move(envAgentImageViews)); } shaderInstanced = Shaders::Phong{Shaders::Phong::Flag::VertexColor | Shaders::Phong::Flag::InstancedTransformation}; shaderInstanced.setShininess(300).setLightPosition({0, 4, 2}).setLightColor(0xaaaaaa_rgbf); shaderInstanced.setDiffuseColor(0xbbbbbb_rgbf); shaderInstanced.setAmbientColor(0x555555_rgbf); shader = Shaders::Phong{}; // meshes { initPrimitives(meshData); for (const auto &[drawable, data] : meshData) meshes[drawable] = MeshTools::compile(data); for (auto &[k, v] : meshes) { instanceBuffers[k] = GL::Buffer{}; v.addVertexBufferInstanced( instanceBuffers[k], 1, 0, Shaders::Phong::TransformationMatrix{}, Shaders::Phong::NormalMatrix{}, Shaders::Phong::Color3{} ); } } // drawables { envDrawables = std::vector(envs.size()); } if (withDebugDraw) { debugDraw = BulletIntegration::DebugDraw{}; debugDraw.setMode(BulletIntegration::DebugDraw::Mode::DrawWireframe); } } MagnumEnvRenderer::Impl::~Impl() { TLOG(INFO) << __PRETTY_FUNCTION__; if (windowlessContextPtr) windowlessContextPtr->makeCurrent(); } RenderingContext * MagnumEnvRenderer::Impl::initContext(RenderingContext *context) { if (context) { // we're given a rendering context } else { // don't have an active rendering context, let's create one TLOG(INFO) << windowlessContextPtr.get(); windowlessContextPtr = std::make_unique(); context = windowlessContextPtr.get(); } return context; } void MagnumEnvRenderer::Impl::reset(Env &env, int envIndex) { ctx->makeCurrent(); // reset renderer data structures { envDrawables[envIndex] = SceneGraph::DrawableGroup3D{}; for (const auto &it : meshes) arrayResize(instanceData[it.first], 0); } // drawables { const auto &drawables = env.getDrawables(); for (auto &it : meshes) { for (const auto &sceneObjectInfo : drawables.at(it.first)) { const auto &color = sceneObjectInfo.color; sceneObjectInfo.objectPtr->addFeature(instanceData[it.first], color, envDrawables[envIndex]); } } } if (withOverviewCamera && envIndex == 0) overview.reset(&env.getScene()); } void MagnumEnvRenderer::Impl::preDraw(Env &, int) { } void MagnumEnvRenderer::Impl::drawAgent(Env &env, int envIndex, int agentIdx, bool readToBuffer) { framebuffer .clearColor(0, Color3{0}) .clearDepth(1.0f) .bind(); for (auto &it : meshes) arrayResize(instanceData[it.first], 0); auto activeCameraPtr = env.getAgents()[agentIdx]->getCamera(); if (withOverviewCamera && overview.enabled && envIndex == 0) activeCameraPtr = overview.camera; // Would be nice to implement frustrum culling here: https://doc.magnum.graphics/magnum/classMagnum_1_1SceneGraph_1_1Drawable.html#SceneGraph-Drawable-draw-order // Although Vulkan renderer is so much faster, who cares activeCameraPtr->draw(envDrawables[envIndex]); shaderInstanced.setProjectionMatrix(activeCameraPtr->projectionMatrix()); // Upload instance data to the GPU (orphaning the previous buffer contents) and draw all meshes in one call for (auto &[drawableType, mesh] : meshes) if (!instanceData[drawableType].empty()) { instanceBuffers[drawableType].setData(instanceData[drawableType], GL::BufferUsage::DynamicDraw); mesh.setInstanceCount(Int(instanceData[drawableType].size())); shaderInstanced.draw(mesh); } // Bullet debug draw if (withDebugDraw) { if (!env.getPhysics().bWorld.getDebugDrawer()) env.getPhysics().bWorld.setDebugDrawer(&debugDraw); GL::Renderer::setDepthFunction(GL::Renderer::DepthFunction::LessOrEqual); debugDraw.setTransformationProjectionMatrix(activeCameraPtr->projectionMatrix()*activeCameraPtr->cameraMatrix()); env.getPhysics().bWorld.debugDrawWorld(); GL::Renderer::setDepthFunction(GL::Renderer::DepthFunction::Less); } if (readToBuffer) { framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{0}); framebuffer.read(framebuffer.viewport(), *agentImageViews[envIndex][agentIdx]); } } void MagnumEnvRenderer::Impl::draw(Envs &envs) { ctx->makeCurrent(); for (int envIdx = 0; envIdx < int(envs.size()); ++envIdx) for (int agentIdx = 0; agentIdx < envs[envIdx]->getNumAgents(); ++agentIdx) drawAgent(*envs[envIdx], envIdx, agentIdx, true); } uint8_t * MagnumEnvRenderer::Impl::getObservation(int envIdx, int agentIdx) { return agentFrames[envIdx][agentIdx].data(); } MagnumEnvRenderer::MagnumEnvRenderer(Envs &envs, int w, int h, bool withDebugDraw, bool withOverview, RenderingContext *ctx) { pimpl = std::make_unique(envs, w, h, withDebugDraw, withOverview, ctx); } MagnumEnvRenderer::~MagnumEnvRenderer() = default; void MagnumEnvRenderer::reset(Env &env, int envIdx) { pimpl->reset(env, envIdx); } void MagnumEnvRenderer::preDraw(Env &env, int envIdx) { pimpl->preDraw(env, envIdx); } void MagnumEnvRenderer::draw(Envs &envs) { pimpl->draw(envs); } void MagnumEnvRenderer::drawAgent(Env &env, int envIndex, int agentIndex, bool readToBuffer) { pimpl->drawAgent(env, envIndex, agentIndex, readToBuffer); } const uint8_t * MagnumEnvRenderer::getObservation(int envIdx, int agentIdx) const { return pimpl->getObservation(envIdx, agentIdx); } Magnum::GL::Framebuffer *MagnumEnvRenderer::getFramebuffer() { return pimpl->getFramebuffer(); } void MagnumEnvRenderer::toggleDebugMode() { pimpl->toggleDebugMode(); } Overview * Megaverse::MagnumEnvRenderer::getOverview() { return pimpl->getOverview(); } ================================================ FILE: src/libs/magnum_rendering/src/rendering_context.cpp ================================================ // Based on code from: // https://devblogs.nvidia.com/parallelforall/egl-eye-opengl-visualization-without-x-server/ // https://github.com/facebookresearch/House3D/blob/master/renderer/gl/glContext.cc // https://github.com/facebookresearch/habitat-sim #include #include #include #include #include #include #if defined(CORRADE_TARGET_APPLE) #include #else #include #include #endif using namespace Megaverse; namespace Mn = Magnum; #if !defined(CORRADE_TARGET_APPLE) constexpr int MAX_DEVICES = 128; #define CHECK_EGL_ERROR() \ do { \ EGLint err = eglGetError(); \ TCHECK(err == EGL_SUCCESS) << "EGL error:" << err; \ } while (0) bool isNvidiaGpuReadable(int device) { const std::string dev = "/dev/nvidia" + std::to_string(device); const int retval = open(dev.c_str(), O_RDONLY); if (retval == -1) return false; close(retval); return true; } struct ContextEGL { explicit ContextEGL(int device) : magnumGlContext_{Mn::NoCreate} { const auto loadStatus = gladLoadEGL(); TCHECK(loadStatus) << "Failed to load EGL " << loadStatus; static const EGLint configAttribs[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE}; // 1. Initialize EGL { EGLDeviceEXT eglDevices[MAX_DEVICES]; EGLint numDevices; eglQueryDevicesEXT(MAX_DEVICES, eglDevices, &numDevices); CHECK_EGL_ERROR(); TCHECK(numDevices > 0) << "[EGL] No devices detected"; TLOG(INFO) << "[EGL] Detected " << numDevices << " EGL devices"; int eglDevId; for (eglDevId = 0; eglDevId < numDevices; ++eglDevId) { EGLAttrib cudaDevNumber; if (eglQueryDeviceAttribEXT(eglDevices[eglDevId], EGL_CUDA_DEVICE_NV, &cudaDevNumber) == EGL_FALSE) continue; if (cudaDevNumber == device) break; } TCHECK(eglDevId < numDevices) << "[EGL] Could not find an EGL device for CUDA device " << device; TCHECK(isNvidiaGpuReadable(eglDevId)) << "[EGL] EGL device " << eglDevId << ", CUDA device " << device << " is not readable"; TLOG(INFO) << "[EGL] Selected EGL device " << eglDevId << " for CUDA device " << device; display_ = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, eglDevices[eglDevId], nullptr); CHECK_EGL_ERROR(); } EGLint major, minor; EGLBoolean retval = eglInitialize(display_, &major, &minor); if (!retval) { TLOG(ERROR) << "[EGL] Failed to initialize."; } CHECK_EGL_ERROR(); TLOG(INFO) << "[EGL] Version: " << eglQueryString(display_, EGL_VERSION) << " display: " << display_; TLOG(INFO) << "[EGL] Vendor: " << eglQueryString(display_, EGL_VENDOR); // 2. Select an appropriate configuration EGLint numConfigs; EGLConfig eglConfig; eglChooseConfig(display_, configAttribs, &eglConfig, 1, &numConfigs); if (numConfigs != 1) { TLOG(ERROR) << "[EGL] Cannot create EGL config. Your driver may " "not support EGL."; } CHECK_EGL_ERROR(); // 3. Bind the API retval = eglBindAPI(EGL_OPENGL_API); if (!retval) { TLOG(ERROR) << "[EGL] failed to bind OpenGL API"; } CHECK_EGL_ERROR(); // 4. Create a context context_ = eglCreateContext(display_, eglConfig, EGL_NO_CONTEXT, NULL); CHECK_EGL_ERROR(); // 5. Make context current and create Magnum context makeCurrent(false); TCHECK(magnumGlContext_.tryCreate()) << "[EGL] Failed to create OpenGL context"; isValid_ = true; }; void makeCurrent(bool updateMagnumContext = true) { if (Magnum::GL::Context::hasCurrent()) Magnum::GL::Context::makeCurrent(nullptr); EGLBoolean retval = eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, context_); if (!retval) TLOG(ERROR) << "[EGL] Failed to make EGL context current"; CHECK_EGL_ERROR(); if (updateMagnumContext) magnumGlContext_.makeCurrent(&magnumGlContext_); }; bool isValid() { return isValid_; }; ~ContextEGL() { eglDestroyContext(display_, context_); // eglTerminate(display_); Magnum::GL::Context::makeCurrent(nullptr); } private: EGLDisplay display_; EGLContext context_; Mn::Platform::GLContext magnumGlContext_; bool isValid_ = false; }; struct WindowlessContext::Impl { explicit Impl(int device) { TLOG(INFO); glContext = std::make_unique(device); makeCurrent(); } void makeCurrent() { glContext->makeCurrent(); } std::unique_ptr glContext{nullptr}; }; #else struct WindowlessContext::Impl { explicit Impl(int device) : magnumGLContext_{Mn::NoCreate}, windowlessGLContext_{Mn::NoCreate} { TLOG(INFO); if (device != 0) TLOG(ERROR) << "Context does not support device specification"; Mn::Platform::WindowlessGLContext::Configuration config; windowlessGLContext_ = Mn::Platform::WindowlessGLContext{config, &magnumGLContext_}; if (!windowlessGLContext_.isCreated()) TLOG(ERROR) << "Unable to create windowless context"; makeCurrent(false); if (!magnumGLContext_.tryCreate()) TLOG(ERROR) << "Unable to create OpenGL context"; } void makeCurrent(bool updateMagnumContext = true) { if (Magnum::GL::Context::hasCurrent()) Magnum::GL::Context::makeCurrent(nullptr); windowlessGLContext_.makeCurrent(); if (updateMagnumContext) magnumGLContext_.makeCurrent(&magnumGLContext_); }; Mn::Platform::GLContext magnumGLContext_; Mn::Platform::WindowlessGLContext windowlessGLContext_; }; #endif WindowlessContext::WindowlessContext(int device /* = 0 */) : pimpl{std::make_unique(device)} {} WindowlessContext::~WindowlessContext() = default; void WindowlessContext::makeCurrent() { pimpl->makeCurrent(); } ================================================ FILE: src/libs/mazes/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.10) project(libmazes VERSION 0.1 LANGUAGES CXX) add_library_default(mazes) target_link_libraries(mazes PUBLIC util) ================================================ FILE: src/libs/mazes/LICENSE.txt ================================================ Copyright (c) 2017 Raziman T. V. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: src/libs/mazes/Readme.md ================================================ # Maze generator **Generate mazes of different shapes and arbitrary sizes** ![Maze types](/examples/mazes.png "Maze types") Maze generator can create rectangular, hexagonal, honeycomb and circular mazes. Maze generation can be done using Kruskal's algorithm, depth-first search, breadth-first search, loop-erased random walk or Prim's algorithm. Mazes can be rendered in svg or png format (using gnuplot as intermediate in the latter case). ## Dependencies Maze generator uses gnuplot (with a system call `gnuplot`) to render png mazes. So make sure that `gnuplot 5.0+` is installed with `pngcairo` terminal support and is in the path if you wish to use png. The code is written in C++ 11, you will need a not-too-ancient C++ compiler to build it. ## Installation ``` cd src; make ``` ## Usage ``` Usage: mazegen [--help] [-m ] [-a ] [-s | -w -h ] [-t ] Optional arguments --help Show this message and exit -m Maze type 0: Rectangular (default) 1: Hexagonal (triangular lattice) 2: Honeycomb 3: Circular 4: Circular (triangular lattice) 5: User-defined -a Algorithm type 0: Kruskal's algorithm (default) 1: Depth-first search 2: Breadth-first search 3: Loop-erased random walk 4: Prim's algorithm -s Size (non-rectangular mazes, default: 20) -w,-h Width and height (rectangular maze, default: 20) -t Output type 0: svg output (default) 1: png output using gnuplot (.plt) intermediate -o Prefix for .svg, .plt and .png outputs (default: maze) ``` ## Issues The arcs in the circular mazes are plotted as parametric curves in gnuplot, and png can take quite long to render for large mazes. ================================================ FILE: src/libs/mazes/examples/gen.sh ================================================ #!/bin/bash ../src/mazegen -m 0 -w 40 -h 40 -a 2 -t 1 -o rectangular_BFS ../src/mazegen -m 1 -s 20 -a 0 -t 0 -o hexagonal_Kruskal ../src/mazegen -m 2 -s 20 -a 3 -t 1 -o honeycomb_LERW ../src/mazegen -m 3 -s 20 -a 1 -t 0 -o circular_DFS ../src/mazegen -m 4 -s 20 -a 0 -o circularhexagon_Kruskal ../src/mazegen -m 5 -a 4 -f manual_input.xy -o manual_Prim ================================================ FILE: src/libs/mazes/examples/manual_input.xy ================================================ 1001 0 65 Line 4.66667 1.33333 5.33333 1.33333 0 153 Line 5.33333 1.33333 6 1.33333 0 252 Line 4.66667 1.33333 4.66667 2 0 684 Line 4.66667 2 5.33333 2 0 685 Line 5.33333 2 6 2 0 761 Line 6 1.33333 6 2 1 128 Line 18 5.33333 18.6667 5.33333 1 300 Line 18.6667 6 19.3333 6 1 488 Line 18 5.33333 18 6 1 674 Line 18.6667 5.33333 19.3333 5.33333 1 897 Line 18 6 18.6667 6 1 969 Line 19.3333 5.33333 19.3333 6 2 28 Line 21.3333 16.6667 21.3333 17.3333 2 120 Line 19.3333 17.3333 21.3333 17.3333 2 218 Line 20.6667 16.6667 21.3333 16.6667 2 641 Line 19.3333 16.6667 20 16.6667 2 697 Line 20 16.6667 20.6667 16.6667 2 929 Line 19.3333 16.6667 19.3333 17.3333 3 66 Line 6.66667 14 6.66667 14.6667 3 99 Line 6.66667 14.6667 7.33333 14.6667 3 222 Line 7.33333 13.3333 7.33333 14.6667 3 728 Line 6.66667 13.3333 7.33333 13.3333 3 784 Line 6.66667 13.3333 6.66667 14 4 25 Line 6 7.33333 6 8 4 60 Line 6 8 6 8.66667 4 269 Line 4.66667 7.33333 6 7.33333 4 747 Line 4.66667 8.66667 5.33333 8.66667 4 748 Line 5.33333 8.66667 6 8.66667 4 852 Line 4.66667 7.33333 4.66667 8 4 853 Line 4.66667 8 4.66667 8.66667 5 52 Line 28.6667 5.33333 29.3333 5.33333 5 56 Line 29.3333 6 30 6 5 520 Line 28.6667 6 29.3333 6 5 564 Line 28.6667 5.33333 28.6667 6 5 583 Line 30 5.33333 30 6 5 886 Line 29.3333 5.33333 30 5.33333 6 82 Line 12 10.6667 12 11.3333 6 93 Line 12 11.3333 13.3333 11.3333 6 284 Line 13.3333 10.6667 13.3333 11.3333 6 936 Line 12 10.6667 12.6667 10.6667 6 947 Line 12.6667 10.6667 13.3333 10.6667 7 445 Line 12 2 12 2.66667 7 489 Line 10.6667 2.66667 11.3333 2.66667 7 523 Line 10.6667 2 10.6667 2.66667 7 554 Line 11.3333 2.66667 12 2.66667 7 688 Line 11.3333 2 12 2 7 742 Line 10.6667 2 11.3333 2 8 22 Line 4 18.6667 4 20.6667 8 94 Line 2.66667 20.6667 4 20.6667 8 154 Line 2.66667 18.6667 4 18.6667 8 231 Line 2.66667 18.6667 2.66667 20 8 981 Line 2.66667 20 2.66667 20.6667 9 85 Line 31.3333 18 32 18 9 91 Line 30.6667 16 32 16 9 104 Line 32 17.3333 32 18 9 108 Line 30.6667 16.6667 30.6667 18 9 141 Line 30.6667 16 30.6667 16.6667 9 228 Line 30.6667 18 31.3333 18 9 288 Line 32 16 32 17.3333 10 126 Line 18 10 18.6667 10 10 289 Line 18 10 18 11.3333 10 464 Line 18 11.3333 18.6667 11.3333 10 793 Line 18.6667 10 18.6667 10.6667 10 794 Line 18.6667 10.6667 18.6667 11.3333 11 69 Line 10 20.6667 13.3333 20.6667 11 192 Line 10.6667 21.3333 12.6667 21.3333 11 215 Line 10 20.6667 10 21.3333 11 234 Line 10 21.3333 10.6667 21.3333 11 422 Line 12.6667 21.3333 13.3333 21.3333 11 443 Line 13.3333 20.6667 13.3333 21.3333 12 81 Line 0.666667 14 2 14 12 204 Line 0.666667 14.6667 2.66667 14.6667 12 292 Line 2.66667 14 2.66667 14.6667 12 475 Line 0.666667 14 0.666667 14.6667 12 614 Line 2 14 2.66667 14 13 188 Line 24.6667 1.33333 24.6667 2.66667 13 619 Line 25.3333 2 25.3333 2.66667 13 704 Line 25.3333 1.33333 25.3333 2 13 760 Line 24.6667 2.66667 25.3333 2.66667 13 943 Line 24.6667 1.33333 25.3333 1.33333 14 -1 Line 0 8 0 12 14 39 Line 0.666667 8 0.666667 9.33333 14 259 Line 0 12 0.666667 12 14 293 Line 0.666667 10.6667 0.666667 12 14 775 Line 0 8 0.666667 8 14 991 Line 0.666667 9.33333 0.666667 10 14 992 Line 0.666667 10 0.666667 10.6667 15 -1 Line 7.33333 22.6667 12.6667 22.6667 15 192 Line 10.6667 22 12.6667 22 15 194 Line 7.33333 22 7.33333 22.6667 15 233 Line 7.33333 22 8.66667 22 15 234 Line 8.66667 22 10.6667 22 15 423 Line 12.6667 22 12.6667 22.6667 16 63 Line 22 14.6667 22.6667 14.6667 16 150 Line 22 13.3333 22.6667 13.3333 16 491 Line 22.6667 13.3333 22.6667 14 16 492 Line 22.6667 14 22.6667 14.6667 16 627 Line 21.3333 13.3333 21.3333 14 16 628 Line 21.3333 14 21.3333 14.6667 16 695 Line 21.3333 14.6667 22 14.6667 16 773 Line 21.3333 13.3333 22 13.3333 17 22 Line 4 20.6667 5.33333 20.6667 17 94 Line 4 20.6667 4 22 17 155 Line 4 22 5.33333 22 17 164 Line 5.33333 21.3333 5.33333 22 17 180 Line 5.33333 20.6667 5.33333 21.3333 18 31 Line 15.3333 20.6667 16.6667 20.6667 18 87 Line 17.3333 20 17.3333 20.6667 18 103 Line 17.3333 19.3333 17.3333 20 18 127 Line 17.3333 17.3333 17.3333 19.3333 18 248 Line 15.3333 20 15.3333 20.6667 18 290 Line 16.6667 20.6667 17.3333 20.6667 18 296 Line 16 17.3333 17.3333 17.3333 18 320 Line 15.3333 18 15.3333 18.6667 18 344 Line 15.3333 18.6667 15.3333 19.3333 18 356 Line 15.3333 17.3333 15.3333 18 18 505 Line 15.3333 17.3333 16 17.3333 18 630 Line 15.3333 19.3333 15.3333 20 19 22 Line 5.33333 18 5.33333 20.6667 19 36 Line 6.66667 18 6.66667 20.6667 19 180 Line 5.33333 20.6667 6.66667 20.6667 19 585 Line 5.33333 18 6 18 19 586 Line 6 18 6.66667 18 20 32 Line 0.666667 18 2 18 20 98 Line 0.666667 20 2 20 20 102 Line 0.666667 18 0.666667 20 20 231 Line 2 18 2 20 21 148 Line 9.33333 11.3333 9.33333 12 21 185 Line 10 10.6667 10 12 21 498 Line 9.33333 12 10 12 21 960 Line 9.33333 10.6667 9.33333 11.3333 21 997 Line 9.33333 10.6667 10 10.6667 22 154 Line 4 18 4 18.6667 22 230 Line 4 18 5.33333 18 23 68 Line 4.66667 13.3333 4.66667 14 23 124 Line 3.33333 14 4.66667 14 23 292 Line 3.33333 13.3333 3.33333 14 23 700 Line 3.33333 13.3333 4 13.3333 23 701 Line 4 13.3333 4.66667 13.3333 24 322 Line 20 12 20.6667 12 24 358 Line 20 11.3333 20 12 24 410 Line 21.3333 11.3333 21.3333 12 24 458 Line 20 11.3333 20.6667 11.3333 24 459 Line 20.6667 11.3333 21.3333 11.3333 24 572 Line 20.6667 12 21.3333 12 25 60 Line 6 8 6.66667 8 25 269 Line 6 6.66667 6 7.33333 25 369 Line 6.66667 6.66667 6.66667 7.33333 25 665 Line 6.66667 7.33333 6.66667 8 25 985 Line 6 6.66667 6.66667 6.66667 26 -1 Line 26.6667 0 28.6667 0 26 167 Line 26.6667 0 26.6667 0.666667 26 177 Line 28 0.666667 28.6667 0.666667 26 631 Line 28.6667 0 28.6667 0.666667 26 814 Line 26.6667 0.666667 27.3333 0.666667 26 815 Line 27.3333 0.666667 28 0.666667 27 74 Line 7.33333 2 8.66667 2 27 153 Line 6.66667 1.33333 8 1.33333 27 279 Line 8.66667 1.33333 8.66667 2 27 303 Line 8 1.33333 8.66667 1.33333 27 449 Line 6.66667 2 7.33333 2 27 761 Line 6.66667 1.33333 6.66667 2 28 120 Line 21.3333 17.3333 21.3333 21.3333 28 156 Line 22 16.6667 22 18 28 218 Line 21.3333 16.6667 22 16.6667 28 271 Line 22 20.6667 22 21.3333 28 514 Line 22 18 22 18.6667 28 558 Line 22 18.6667 22 19.3333 28 872 Line 22 19.3333 22 20 28 873 Line 22 20 22 20.6667 28 996 Line 21.3333 21.3333 22 21.3333 29 91 Line 32 13.3333 32 15.3333 29 201 Line 32.6667 12 32.6667 13.3333 29 205 Line 32.6667 13.3333 32.6667 14.6667 29 229 Line 32 12 32 13.3333 29 275 Line 32 12 32.6667 12 29 870 Line 32 15.3333 32.6667 15.3333 29 891 Line 32.6667 14.6667 32.6667 15.3333 30 83 Line 13.3333 5.33333 13.3333 6.66667 30 506 Line 14 5.33333 14 6 30 541 Line 14 6 14 6.66667 30 661 Line 13.3333 6.66667 14 6.66667 30 714 Line 13.3333 5.33333 14 5.33333 31 -1 Line 15.3333 22.6667 16.6667 22.6667 31 248 Line 15.3333 20.6667 15.3333 21.3333 31 287 Line 15.3333 21.3333 15.3333 22.6667 31 290 Line 16.6667 20.6667 16.6667 22.6667 32 -1 Line 0 17.3333 0 18 32 102 Line 0 18 0.666667 18 32 244 Line 0 17.3333 2 17.3333 32 690 Line 2 17.3333 2 18 33 53 Line 32 1.33333 32 2.66667 33 96 Line 29.3333 1.33333 32 1.33333 33 161 Line 29.3333 2.66667 32 2.66667 33 312 Line 29.3333 1.33333 29.3333 2 33 915 Line 29.3333 2 29.3333 2.66667 34 239 Line 4 6 5.33333 6 34 278 Line 5.33333 5.33333 5.33333 6 34 485 Line 4 5.33333 4.66667 5.33333 34 486 Line 4.66667 5.33333 5.33333 5.33333 34 549 Line 4 5.33333 4 6 35 73 Line 24 6 24.6667 6 35 209 Line 22.6667 6.66667 24.6667 6.66667 35 277 Line 24.6667 6 24.6667 6.66667 35 305 Line 22.6667 6 24 6 35 789 Line 22.6667 6 22.6667 6.66667 36 49 Line 6.66667 20.6667 8.66667 20.6667 36 69 Line 8.66667 18 8.66667 20.6667 36 893 Line 8 18 8.66667 18 36 894 Line 6.66667 18 7.33333 18 36 895 Line 7.33333 18 8 18 37 384 Line 16 7.33333 16.6667 7.33333 37 607 Line 16 6 16 6.66667 37 745 Line 16.6667 6 16.6667 6.66667 37 746 Line 16.6667 6.66667 16.6667 7.33333 37 874 Line 16 6.66667 16 7.33333 37 876 Line 16 6 16.6667 6 38 39 Line 1.33333 9.33333 2 9.33333 38 206 Line 2 9.33333 2 10.6667 38 236 Line 2 10.6667 2 11.3333 38 293 Line 1.33333 10.6667 1.33333 11.3333 38 813 Line 1.33333 11.3333 2 11.3333 38 991 Line 1.33333 9.33333 1.33333 10 38 992 Line 1.33333 10 1.33333 10.6667 39 206 Line 2 8.66667 2 9.33333 39 245 Line 0.666667 7.33333 2 7.33333 39 596 Line 2 7.33333 2 8 39 597 Line 2 8 2 8.66667 39 775 Line 0.666667 7.33333 0.666667 8 39 991 Line 0.666667 9.33333 1.33333 9.33333 40 549 Line 3.33333 5.33333 3.33333 6 40 550 Line 3.33333 6 3.33333 6.66667 40 634 Line 2.66667 5.33333 3.33333 5.33333 40 635 Line 2.66667 6.66667 3.33333 6.66667 40 902 Line 2.66667 5.33333 2.66667 6 40 903 Line 2.66667 6 2.66667 6.66667 41 283 Line 16 11.3333 16 12 41 326 Line 15.3333 13.3333 16 13.3333 41 388 Line 16 12.6667 16 13.3333 41 426 Line 15.3333 11.3333 16 11.3333 41 569 Line 15.3333 12.6667 15.3333 13.3333 41 738 Line 15.3333 11.3333 15.3333 12 41 739 Line 15.3333 12 15.3333 12.6667 41 913 Line 16 12 16 12.6667 42 -1 Line 0 2.66667 0 5.33333 42 182 Line 0 5.33333 0.666667 5.33333 42 598 Line 0 2.66667 0.666667 2.66667 42 977 Line 0.666667 2.66667 0.666667 3.33333 42 978 Line 0.666667 3.33333 0.666667 4 42 979 Line 0.666667 4 0.666667 4.66667 42 980 Line 0.666667 4.66667 0.666667 5.33333 43 57 Line 14.6667 7.33333 14.6667 8 43 272 Line 14.6667 7.33333 15.3333 7.33333 43 360 Line 14.6667 8.66667 15.3333 8.66667 43 875 Line 15.3333 7.33333 15.3333 8 43 905 Line 15.3333 8 15.3333 8.66667 43 949 Line 14.6667 8 14.6667 8.66667 44 -1 Line 19.3333 22.6667 20.6667 22.6667 44 120 Line 19.3333 21.3333 20.6667 21.3333 44 637 Line 19.3333 22 19.3333 22.6667 44 795 Line 20.6667 21.3333 20.6667 22 44 796 Line 20.6667 22 20.6667 22.6667 44 951 Line 19.3333 21.3333 19.3333 22 45 105 Line 25.3333 13.3333 26.6667 13.3333 45 759 Line 25.3333 12.6667 25.3333 13.3333 45 889 Line 25.3333 12.6667 26 12.6667 45 890 Line 26 12.6667 26.6667 12.6667 45 967 Line 26.6667 12.6667 26.6667 13.3333 46 64 Line 15.3333 4.66667 16 4.66667 46 232 Line 15.3333 4.66667 15.3333 5.33333 46 420 Line 15.3333 5.33333 16 5.33333 46 663 Line 16 4.66667 16.6667 4.66667 46 677 Line 16.6667 4.66667 16.6667 5.33333 46 876 Line 16 5.33333 16.6667 5.33333 47 -1 Line 13.3333 22.6667 14 22.6667 47 287 Line 14 21.3333 14 22.6667 47 422 Line 13.3333 21.3333 13.3333 22 47 423 Line 13.3333 22 13.3333 22.6667 47 443 Line 13.3333 21.3333 14 21.3333 48 143 Line 21.3333 8 21.3333 8.66667 48 146 Line 21.3333 8 22.6667 8 48 439 Line 22.6667 9.33333 23.3333 9.33333 48 468 Line 21.3333 9.33333 22 9.33333 48 469 Line 22 9.33333 22.6667 9.33333 48 682 Line 23.3333 8 23.3333 8.66667 48 683 Line 23.3333 8.66667 23.3333 9.33333 48 812 Line 22.6667 8 23.3333 8 48 974 Line 21.3333 8.66667 21.3333 9.33333 49 164 Line 6.66667 21.3333 7.33333 21.3333 49 180 Line 6.66667 20.6667 6.66667 21.3333 49 215 Line 8.66667 20.6667 8.66667 21.3333 49 233 Line 7.33333 21.3333 8.66667 21.3333 50 92 Line 30 9.33333 30 10 50 255 Line 30 9.33333 31.3333 9.33333 50 652 Line 31.3333 9.33333 31.3333 10 50 920 Line 30.6667 10 31.3333 10 50 937 Line 30 10 30.6667 10 51 -1 Line 0 20.6667 0 21.3333 51 98 Line 0.666667 20.6667 1.33333 20.6667 51 159 Line 1.33333 20.6667 1.33333 21.3333 51 173 Line 0 21.3333 1.33333 21.3333 51 907 Line 0 20.6667 0.666667 20.6667 52 298 Line 28.6667 4 28.6667 5.33333 52 818 Line 28.6667 3.33333 28.6667 4 52 843 Line 29.3333 3.33333 29.3333 4 52 885 Line 29.3333 4 29.3333 4.66667 52 886 Line 29.3333 4.66667 29.3333 5.33333 52 916 Line 28.6667 3.33333 29.3333 3.33333 53 -1 Line 34 0 34 2.66667 53 -1 Line 32 0 34 0 53 96 Line 32 0 32 1.33333 53 161 Line 32 2.66667 34 2.66667 54 621 Line 13.3333 8.66667 13.3333 9.33333 54 726 Line 12 8.66667 12 9.33333 54 780 Line 12 8.66667 12.6667 8.66667 54 781 Line 12.6667 8.66667 13.3333 8.66667 54 935 Line 12 9.33333 12.6667 9.33333 54 946 Line 12.6667 9.33333 13.3333 9.33333 55 62 Line 10.6667 13.3333 11.3333 13.3333 55 339 Line 10.6667 14 11.3333 14 55 502 Line 11.3333 13.3333 12 13.3333 55 605 Line 11.3333 14 12 14 55 811 Line 12 13.3333 12 14 55 955 Line 10.6667 13.3333 10.6667 14 56 121 Line 30.6667 6 30.6667 7.33333 56 520 Line 29.3333 6 29.3333 6.66667 56 521 Line 29.3333 6.66667 29.3333 7.33333 56 583 Line 30 6 30.6667 6 56 622 Line 29.3333 7.33333 30 7.33333 56 623 Line 30 7.33333 30.6667 7.33333 57 272 Line 14.6667 6.66667 14.6667 7.33333 57 438 Line 14 7.33333 14 8 57 541 Line 14 6.66667 14.6667 6.66667 57 661 Line 14 6.66667 14 7.33333 57 949 Line 14 8 14.6667 8 58 127 Line 17.3333 17.3333 18.6667 17.3333 58 238 Line 17.3333 16.6667 18 16.6667 58 253 Line 18 16.6667 18.6667 16.6667 58 296 Line 17.3333 16.6667 17.3333 17.3333 58 929 Line 18.6667 16.6667 18.6667 17.3333 59 140 Line 28 21.3333 28.6667 21.3333 59 171 Line 29.3333 20.6667 29.3333 21.3333 59 225 Line 28 19.3333 28 20 59 242 Line 28 19.3333 29.3333 19.3333 59 247 Line 29.3333 19.3333 29.3333 20.6667 59 257 Line 28 20 28 21.3333 59 304 Line 28.6667 21.3333 29.3333 21.3333 60 589 Line 6.66667 8.66667 6.66667 9.33333 60 601 Line 6.66667 8 6.66667 8.66667 60 617 Line 6 9.33333 6.66667 9.33333 60 748 Line 6 8.66667 6 9.33333 61 405 Line 18 12.6667 18.6667 12.6667 61 446 Line 18.6667 12.6667 18.6667 13.3333 61 447 Line 18.6667 13.3333 18.6667 14 61 466 Line 18 14 18.6667 14 61 577 Line 18 12.6667 18 13.3333 61 578 Line 18 13.3333 18 14 62 107 Line 10.6667 12.6667 11.3333 12.6667 62 144 Line 9.33333 13.3333 10 13.3333 62 321 Line 9.33333 12.6667 9.33333 13.3333 62 498 Line 9.33333 12.6667 10 12.6667 62 502 Line 11.3333 12.6667 11.3333 13.3333 62 774 Line 10 12.6667 10.6667 12.6667 62 955 Line 10 13.3333 10.6667 13.3333 63 156 Line 22 16 22.6667 16 63 695 Line 22 14.6667 22 15.3333 63 696 Line 22 15.3333 22 16 63 849 Line 22.6667 14.6667 22.6667 15.3333 63 850 Line 22.6667 15.3333 22.6667 16 64 232 Line 15.3333 4 15.3333 4.66667 64 494 Line 15.3333 3.33333 15.3333 4 64 610 Line 15.3333 3.33333 16 3.33333 64 662 Line 16 3.33333 16 4 64 663 Line 16 4 16 4.66667 65 -1 Line 4.66667 0 5.33333 0 65 153 Line 5.33333 0.666667 5.33333 1.33333 65 165 Line 5.33333 0 5.33333 0.666667 65 993 Line 4.66667 0 4.66667 0.666667 65 994 Line 4.66667 0.666667 4.66667 1.33333 66 68 Line 4.66667 14 6 14 66 99 Line 5.33333 14.6667 6.66667 14.6667 66 124 Line 4.66667 14 4.66667 14.6667 66 286 Line 4.66667 14.6667 5.33333 14.6667 66 784 Line 6 14 6.66667 14 67 86 Line 26.6667 4 27.3333 4 67 113 Line 28 2 28 3.33333 67 177 Line 28 1.33333 28 2 67 507 Line 27.3333 4 28 4 67 576 Line 26.6667 2.66667 26.6667 3.33333 67 814 Line 26.6667 1.33333 27.3333 1.33333 67 815 Line 27.3333 1.33333 28 1.33333 67 818 Line 28 3.33333 28 4 67 821 Line 26.6667 3.33333 26.6667 4 67 961 Line 26.6667 1.33333 26.6667 2 67 962 Line 26.6667 2 26.6667 2.66667 68 112 Line 4.66667 13.3333 6 13.3333 68 784 Line 6 13.3333 6 14 69 134 Line 9.33333 17.3333 13.3333 17.3333 69 215 Line 8.66667 20.6667 10 20.6667 69 221 Line 13.3333 17.3333 13.3333 19.3333 69 323 Line 8.66667 17.3333 9.33333 17.3333 69 331 Line 13.3333 20 13.3333 20.6667 69 537 Line 13.3333 19.3333 13.3333 20 69 893 Line 8.66667 17.3333 8.66667 18 70 99 Line 5.33333 16 6.66667 16 70 176 Line 5.33333 16.6667 6.66667 16.6667 70 529 Line 5.33333 16 5.33333 16.6667 70 792 Line 6.66667 16 6.66667 16.6667 71 144 Line 9.33333 14.6667 10 14.6667 71 168 Line 9.33333 14.6667 9.33333 15.3333 71 340 Line 9.33333 15.3333 10 15.3333 71 394 Line 10.6667 14.6667 10.6667 15.3333 71 544 Line 10 15.3333 10.6667 15.3333 71 956 Line 10 14.6667 10.6667 14.6667 72 -1 Line 20.6667 0 21.3333 0 72 202 Line 20.6667 0 20.6667 3.33333 72 246 Line 21.3333 2.66667 21.3333 3.33333 72 249 Line 21.3333 0 21.3333 2.66667 72 941 Line 20.6667 3.33333 21.3333 3.33333 73 277 Line 24.6667 6 26 6 73 305 Line 24 5.33333 24 6 73 361 Line 26 6 26.6667 6 73 451 Line 26.6667 5.33333 26.6667 6 73 798 Line 24.6667 5.33333 25.3333 5.33333 73 803 Line 25.3333 5.33333 26 5.33333 73 804 Line 26 5.33333 26.6667 5.33333 73 857 Line 24 5.33333 24.6667 5.33333 74 213 Line 8.66667 2 8.66667 2.66667 74 449 Line 7.33333 2 7.33333 2.66667 74 522 Line 7.33333 2.66667 8 2.66667 74 787 Line 8 2.66667 8.66667 2.66667 75 225 Line 26 20 27.3333 20 75 256 Line 26 21.3333 27.3333 21.3333 75 257 Line 27.3333 20 27.3333 21.3333 75 299 Line 26 20 26 21.3333 76 371 Line 19.3333 14 20 14 76 447 Line 18.6667 14 19.3333 14 76 466 Line 18.6667 14 18.6667 14.6667 76 628 Line 20.6667 14 20.6667 14.6667 76 670 Line 20 14 20.6667 14 76 901 Line 18.6667 14.6667 19.3333 14.6667 76 918 Line 19.3333 14.6667 20 14.6667 76 944 Line 20 14.6667 20.6667 14.6667 77 -1 Line 0 0 1.33333 0 77 130 Line 1.33333 0 1.33333 0.666667 77 261 Line 0 0.666667 1.33333 0.666667 78 126 Line 18.6667 8.66667 18.6667 10 78 175 Line 19.3333 8.66667 19.3333 9.33333 78 793 Line 18.6667 10 19.3333 10 78 810 Line 18.6667 8.66667 19.3333 8.66667 78 982 Line 19.3333 9.33333 19.3333 10 79 212 Line 24 18.6667 24 20 79 271 Line 22.6667 20.6667 24 20.6667 79 477 Line 22.6667 18.6667 23.3333 18.6667 79 478 Line 23.3333 18.6667 24 18.6667 79 558 Line 22.6667 18.6667 22.6667 19.3333 79 869 Line 24 20 24 20.6667 79 872 Line 22.6667 19.3333 22.6667 20 79 873 Line 22.6667 20 22.6667 20.6667 80 -1 Line 34 3.33333 34 4 80 161 Line 31.3333 3.33333 34 3.33333 80 315 Line 31.3333 3.33333 31.3333 4 80 790 Line 33.3333 4 34 4 80 839 Line 32.6667 4 33.3333 4 80 854 Line 31.3333 4 32 4 80 855 Line 32 4 32.6667 4 81 -1 Line 0 13.3333 0 14 81 259 Line 0 13.3333 0.666667 13.3333 81 475 Line 0 14 0.666667 14 81 614 Line 2 13.3333 2 14 81 776 Line 0.666667 13.3333 1.33333 13.3333 81 777 Line 1.33333 13.3333 2 13.3333 82 185 Line 10.6667 10.6667 10.6667 11.3333 82 841 Line 10.6667 10.6667 11.3333 10.6667 82 842 Line 11.3333 10.6667 12 10.6667 82 999 Line 10.6667 11.3333 11.3333 11.3333 82 1000 Line 11.3333 11.3333 12 11.3333 83 324 Line 12.6667 7.33333 13.3333 7.33333 83 401 Line 12.6667 6.66667 12.6667 7.33333 83 418 Line 12.6667 5.33333 13.3333 5.33333 83 524 Line 12.6667 5.33333 12.6667 6 83 525 Line 12.6667 6 12.6667 6.66667 83 661 Line 13.3333 6.66667 13.3333 7.33333 84 255 Line 29.3333 8.66667 30.6667 8.66667 84 295 Line 30.6667 8 30.6667 8.66667 84 516 Line 29.3333 8 29.3333 8.66667 84 622 Line 29.3333 8 30 8 84 623 Line 30 8 30.6667 8 85 104 Line 32 18 33.3333 18 85 191 Line 33.3333 18 33.3333 19.3333 85 211 Line 32 19.3333 33.3333 19.3333 85 224 Line 31.3333 19.3333 32 19.3333 85 228 Line 31.3333 18 31.3333 19.3333 86 451 Line 26.6667 5.33333 27.3333 5.33333 86 507 Line 27.3333 4 27.3333 4.66667 86 508 Line 27.3333 4.66667 27.3333 5.33333 86 804 Line 26.6667 4.66667 26.6667 5.33333 86 822 Line 26.6667 4 26.6667 4.66667 87 -1 Line 17.3333 22.6667 18.6667 22.6667 87 103 Line 17.3333 20 18.6667 20 87 280 Line 18.6667 20 18.6667 20.6667 87 290 Line 17.3333 20.6667 17.3333 22.6667 87 637 Line 18.6667 22 18.6667 22.6667 87 950 Line 18.6667 20.6667 18.6667 21.3333 87 951 Line 18.6667 21.3333 18.6667 22 88 -1 Line 34 5.33333 34 6.66667 88 170 Line 33.3333 5.33333 33.3333 6.66667 88 536 Line 33.3333 6.66667 34 6.66667 88 791 Line 33.3333 5.33333 34 5.33333 89 725 Line 10.6667 9.33333 11.3333 9.33333 89 726 Line 11.3333 9.33333 12 9.33333 89 740 Line 10.6667 9.33333 10.6667 10 89 841 Line 10.6667 10 11.3333 10 89 842 Line 11.3333 10 12 10 89 935 Line 12 9.33333 12 10 90 -1 Line 17.3333 0 19.3333 0 90 151 Line 19.3333 0 19.3333 1.33333 90 162 Line 17.3333 1.33333 18.6667 1.33333 90 254 Line 18.6667 1.33333 19.3333 1.33333 90 723 Line 17.3333 0 17.3333 0.666667 90 724 Line 17.3333 0.666667 17.3333 1.33333 91 118 Line 30 14 30 16 91 131 Line 30 13.3333 30 14 91 135 Line 30 13.3333 30.6667 13.3333 91 141 Line 30 16 30.6667 16 91 229 Line 30.6667 13.3333 32 13.3333 91 870 Line 32 15.3333 32 16 92 220 Line 28.6667 10.6667 28.6667 11.3333 92 255 Line 29.3333 9.33333 30 9.33333 92 644 Line 28.6667 9.33333 28.6667 10 92 825 Line 28.6667 11.3333 29.3333 11.3333 92 826 Line 29.3333 11.3333 30 11.3333 92 904 Line 28.6667 10 28.6667 10.6667 92 917 Line 28.6667 9.33333 29.3333 9.33333 92 937 Line 30 10 30 10.6667 92 938 Line 30 10.6667 30 11.3333 93 553 Line 13.3333 11.3333 13.3333 12 93 608 Line 12 12 12.6667 12 93 609 Line 12.6667 12 13.3333 12 93 1000 Line 12 11.3333 12 12 94 216 Line 2.66667 20.6667 2.66667 22 94 219 Line 2.66667 22 4 22 95 138 Line 2.66667 1.33333 2.66667 2.66667 95 152 Line 3.33333 2.66667 3.33333 3.33333 95 187 Line 2.66667 3.33333 3.33333 3.33333 95 252 Line 3.33333 1.33333 3.33333 2 95 268 Line 3.33333 0.666667 3.33333 1.33333 95 274 Line 2.66667 0.666667 2.66667 1.33333 95 281 Line 2.66667 2.66667 2.66667 3.33333 95 624 Line 2.66667 0.666667 3.33333 0.666667 95 686 Line 3.33333 2 3.33333 2.66667 96 -1 Line 29.3333 0 32 0 96 312 Line 29.3333 0.666667 29.3333 1.33333 96 631 Line 29.3333 0 29.3333 0.666667 97 254 Line 18.6667 4 19.3333 4 97 674 Line 18.6667 4.66667 19.3333 4.66667 97 675 Line 19.3333 4.66667 20 4.66667 97 809 Line 18.6667 4 18.6667 4.66667 97 837 Line 20 4 20 4.66667 97 948 Line 19.3333 4 20 4 98 159 Line 1.33333 20.6667 2 20.6667 98 907 Line 0.666667 20 0.666667 20.6667 98 981 Line 2 20 2 20.6667 99 210 Line 7.33333 15.3333 7.33333 16 99 286 Line 5.33333 14.6667 5.33333 16 99 309 Line 7.33333 14.6667 7.33333 15.3333 99 792 Line 6.66667 16 7.33333 16 100 -1 Line 34 7.33333 34 8.66667 100 183 Line 33.3333 8.66667 34 8.66667 100 536 Line 33.3333 7.33333 34 7.33333 100 971 Line 33.3333 7.33333 33.3333 8 100 972 Line 33.3333 8 33.3333 8.66667 101 151 Line 19.3333 1.33333 20 1.33333 101 202 Line 20 1.33333 20 3.33333 101 254 Line 19.3333 1.33333 19.3333 3.33333 101 948 Line 19.3333 3.33333 20 3.33333 102 -1 Line 0 18 0 20 102 907 Line 0 20 0.666667 20 103 127 Line 17.3333 19.3333 18.6667 19.3333 103 280 Line 18.6667 19.3333 18.6667 20 104 -1 Line 34 17.3333 34 18 104 139 Line 33.3333 17.3333 34 17.3333 104 186 Line 32.6667 17.3333 33.3333 17.3333 104 191 Line 33.3333 18 34 18 104 288 Line 32 17.3333 32.6667 17.3333 105 208 Line 26.6667 13.3333 26.6667 14 105 763 Line 26 14 26.6667 14 105 765 Line 25.3333 14 26 14 105 848 Line 25.3333 13.3333 25.3333 14 106 117 Line 10 0.666667 11.3333 0.666667 106 279 Line 10 1.33333 10.6667 1.33333 106 303 Line 10 0.666667 10 1.33333 106 402 Line 11.3333 0.666667 11.3333 1.33333 106 742 Line 10.6667 1.33333 11.3333 1.33333 107 502 Line 11.3333 12.6667 12 12.6667 107 608 Line 12 12 12 12.6667 107 774 Line 10.6667 12 10.6667 12.6667 107 999 Line 10.6667 12 11.3333 12 107 1000 Line 11.3333 12 12 12 108 141 Line 29.3333 16.6667 30.6667 16.6667 108 207 Line 29.3333 16.6667 29.3333 17.3333 108 228 Line 30.6667 18 30.6667 19.3333 108 242 Line 29.3333 17.3333 29.3333 19.3333 108 247 Line 29.3333 19.3333 30 19.3333 108 276 Line 30 19.3333 30.6667 19.3333 109 587 Line 7.33333 4.66667 8 4.66667 109 588 Line 8 4.66667 8.66667 4.66667 109 646 Line 8.66667 4 8.66667 4.66667 109 816 Line 7.33333 4 8 4 109 817 Line 8 4 8.66667 4 109 831 Line 7.33333 4 7.33333 4.66667 110 114 Line 28 14.6667 28 16 110 208 Line 26.6667 14.6667 27.3333 14.6667 110 264 Line 26.6667 16 27.3333 16 110 266 Line 27.3333 14.6667 28 14.6667 110 270 Line 27.3333 16 28 16 110 313 Line 26.6667 14.6667 26.6667 16 111 -1 Line 0 22 0 22.6667 111 -1 Line 0 22.6667 1.33333 22.6667 111 145 Line 1.33333 22 1.33333 22.6667 111 173 Line 0 22 1.33333 22 112 237 Line 4.66667 12.6667 5.33333 12.6667 112 479 Line 5.33333 12.6667 6 12.6667 112 701 Line 4.66667 12.6667 4.66667 13.3333 112 727 Line 6 12.6667 6 13.3333 113 177 Line 28 2 28.6667 2 113 818 Line 28 3.33333 28.6667 3.33333 113 915 Line 28.6667 2 28.6667 2.66667 113 916 Line 28.6667 2.66667 28.6667 3.33333 114 118 Line 28.6667 14 28.6667 16 114 131 Line 28 14 28.6667 14 114 207 Line 28 16 28.6667 16 114 266 Line 28 14 28 14.6667 115 122 Line 22 2.66667 22.6667 2.66667 115 188 Line 22.6667 2.66667 24 2.66667 115 217 Line 22 4.66667 22.6667 4.66667 115 246 Line 22 2.66667 22 4 115 307 Line 24 2.66667 24 4 115 856 Line 23.3333 4.66667 24 4.66667 115 858 Line 24 4 24 4.66667 115 954 Line 22 4 22 4.66667 115 984 Line 22.6667 4.66667 23.3333 4.66667 116 237 Line 4 12 5.33333 12 116 435 Line 5.33333 11.3333 5.33333 12 116 574 Line 4 11.3333 4.66667 11.3333 116 575 Line 4.66667 11.3333 5.33333 11.3333 116 805 Line 4 11.3333 4 12 117 -1 Line 10 0 11.3333 0 117 165 Line 10 0 10 0.666667 117 715 Line 11.3333 0 11.3333 0.666667 118 131 Line 28.6667 14 30 14 118 141 Line 29.3333 16 30 16 118 207 Line 28.6667 16 29.3333 16 119 329 Line 22.6667 10.6667 22.6667 11.3333 119 330 Line 22.6667 11.3333 22.6667 12 119 333 Line 22.6667 10.6667 23.3333 10.6667 119 470 Line 23.3333 10.6667 24 10.6667 119 638 Line 22.6667 12 23.3333 12 119 878 Line 24 10.6667 24 11.3333 119 879 Line 24 11.3333 24 12 119 986 Line 23.3333 12 24 12 120 258 Line 19.3333 17.3333 19.3333 19.3333 120 280 Line 19.3333 19.3333 19.3333 20.6667 120 795 Line 20.6667 21.3333 21.3333 21.3333 120 950 Line 19.3333 20.6667 19.3333 21.3333 121 129 Line 31.3333 6 31.3333 7.33333 121 295 Line 30.6667 7.33333 31.3333 7.33333 121 584 Line 30.6667 6 31.3333 6 122 188 Line 22.6667 1.33333 22.6667 2.66667 122 249 Line 22 1.33333 22 2.66667 122 314 Line 22 1.33333 22.6667 1.33333 123 -1 Line 29.3333 22.6667 30.6667 22.6667 123 171 Line 29.3333 21.3333 30.6667 21.3333 123 304 Line 29.3333 21.3333 29.3333 22.6667 123 311 Line 30.6667 21.3333 30.6667 22.6667 124 195 Line 3.33333 15.3333 4.66667 15.3333 124 286 Line 4.66667 14.6667 4.66667 15.3333 124 292 Line 3.33333 14 3.33333 14.6667 124 737 Line 3.33333 14.6667 3.33333 15.3333 125 328 Line 27.3333 6 28 6 125 363 Line 27.3333 7.33333 28 7.33333 125 428 Line 27.3333 6 27.3333 6.66667 125 429 Line 27.3333 6.66667 27.3333 7.33333 125 495 Line 28 6 28 6.66667 125 496 Line 28 6.66667 28 7.33333 126 172 Line 18 8 18 9.33333 126 810 Line 18.6667 8 18.6667 8.66667 126 896 Line 18 8 18.6667 8 126 998 Line 18 9.33333 18 10 127 258 Line 18.6667 17.3333 18.6667 19.3333 128 488 Line 17.3333 5.33333 18 5.33333 128 674 Line 18.6667 4.66667 18.6667 5.33333 128 677 Line 17.3333 4.66667 17.3333 5.33333 128 808 Line 17.3333 4.66667 18 4.66667 128 809 Line 18 4.66667 18.6667 4.66667 129 163 Line 32 6.66667 32 8.66667 129 255 Line 31.3333 8.66667 31.3333 9.33333 129 260 Line 32 5.33333 32 6.66667 129 295 Line 31.3333 7.33333 31.3333 8.66667 129 302 Line 31.3333 5.33333 32 5.33333 129 584 Line 31.3333 5.33333 31.3333 6 129 652 Line 31.3333 9.33333 32 9.33333 129 888 Line 32 8.66667 32 9.33333 130 -1 Line 1.33333 0 2.66667 0 130 274 Line 1.33333 0.666667 2.66667 0.666667 130 624 Line 2.66667 0 2.66667 0.666667 131 160 Line 28 13.3333 29.3333 13.3333 131 266 Line 28 13.3333 28 14 131 717 Line 29.3333 13.3333 30 13.3333 132 157 Line 0.666667 15.3333 0.666667 16 132 204 Line 0.666667 15.3333 2.66667 15.3333 132 241 Line 1.33333 16 2.66667 16 132 467 Line 2.66667 15.3333 2.66667 16 132 711 Line 0.666667 16 1.33333 16 133 353 Line 7.33333 12 8 12 133 461 Line 6.66667 11.3333 6.66667 12 133 519 Line 6.66667 12 7.33333 12 133 823 Line 6.66667 11.3333 7.33333 11.3333 133 824 Line 7.33333 11.3333 8 11.3333 133 975 Line 8 11.3333 8 12 134 221 Line 13.3333 17.3333 14 17.3333 134 226 Line 11.3333 16.6667 12.6667 16.6667 134 267 Line 12.6667 16.6667 14 16.6667 134 282 Line 10 16.6667 11.3333 16.6667 134 294 Line 14 16.6667 14 17.3333 134 323 Line 9.33333 16.6667 9.33333 17.3333 134 797 Line 9.33333 16.6667 10 16.6667 135 229 Line 30.6667 12 30.6667 13.3333 135 632 Line 30 12 30.6667 12 135 717 Line 30 12.6667 30 13.3333 135 959 Line 30 12 30 12.6667 136 147 Line 28 12 28 12.6667 136 220 Line 28 11.3333 28 12 136 262 Line 26.6667 11.3333 27.3333 11.3333 136 890 Line 26.6667 12 26.6667 12.6667 136 931 Line 26.6667 11.3333 26.6667 12 136 964 Line 27.3333 11.3333 28 11.3333 136 967 Line 26.6667 12.6667 27.3333 12.6667 136 968 Line 27.3333 12.6667 28 12.6667 137 363 Line 27.3333 8 28 8 137 476 Line 27.3333 9.33333 27.3333 10 137 615 Line 28 8.66667 28 9.33333 137 644 Line 28 9.33333 28 10 137 691 Line 27.3333 8 27.3333 8.66667 137 692 Line 27.3333 8.66667 27.3333 9.33333 137 845 Line 28 8 28 8.66667 137 963 Line 27.3333 10 28 10 138 261 Line 1.33333 1.33333 1.33333 2 138 274 Line 1.33333 1.33333 2.66667 1.33333 138 281 Line 2 2.66667 2.66667 2.66667 138 599 Line 1.33333 2 1.33333 2.66667 138 636 Line 1.33333 2.66667 2 2.66667 139 -1 Line 34 16 34 17.3333 139 186 Line 33.3333 16 33.3333 17.3333 139 912 Line 33.3333 16 34 16 140 184 Line 27.3333 22 28.6667 22 140 256 Line 27.3333 21.3333 27.3333 22 140 257 Line 27.3333 21.3333 28 21.3333 140 304 Line 28.6667 21.3333 28.6667 22 141 207 Line 29.3333 16 29.3333 16.6667 142 262 Line 26.6667 10 26.6667 10.6667 142 379 Line 24.6667 9.33333 25.3333 9.33333 142 380 Line 25.3333 9.33333 26 9.33333 142 395 Line 24.6667 9.33333 24.6667 10 142 471 Line 24.6667 10 24.6667 10.6667 142 476 Line 26.6667 9.33333 26.6667 10 142 499 Line 24.6667 10.6667 25.3333 10.6667 142 594 Line 26 9.33333 26.6667 9.33333 142 930 Line 26 10.6667 26.6667 10.6667 142 932 Line 25.3333 10.6667 26 10.6667 143 146 Line 20.6667 8 21.3333 8 143 175 Line 20 8 20 8.66667 143 227 Line 20 8 20.6667 8 143 973 Line 20 8.66667 20.6667 8.66667 143 974 Line 20.6667 8.66667 21.3333 8.66667 144 899 Line 9.33333 13.3333 9.33333 14 144 900 Line 9.33333 14 9.33333 14.6667 144 955 Line 10 13.3333 10 14 144 956 Line 10 14 10 14.6667 145 -1 Line 1.33333 22.6667 2.66667 22.6667 145 159 Line 1.33333 22 2 22 145 216 Line 2 22 2.66667 22 145 219 Line 2.66667 22 2.66667 22.6667 146 227 Line 20.6667 7.33333 20.6667 8 146 301 Line 20.6667 7.33333 22 7.33333 146 812 Line 22.6667 7.33333 22.6667 8 146 934 Line 22 7.33333 22.6667 7.33333 147 160 Line 28 12.6667 29.3333 12.6667 147 220 Line 28 12 28.6667 12 147 825 Line 28.6667 12 29.3333 12 147 959 Line 29.3333 12 29.3333 12.6667 148 318 Line 8.66667 12 8.66667 12.6667 148 321 Line 8.66667 12.6667 9.33333 12.6667 148 498 Line 9.33333 12 9.33333 12.6667 148 960 Line 8.66667 11.3333 9.33333 11.3333 148 975 Line 8.66667 11.3333 8.66667 12 149 189 Line 20 6 20 6.66667 149 227 Line 20 6.66667 20 7.33333 149 300 Line 19.3333 6 19.3333 7.33333 149 768 Line 19.3333 7.33333 20 7.33333 149 969 Line 19.3333 6 20 6 150 265 Line 22 12.6667 22.6667 12.6667 150 491 Line 22.6667 13.3333 23.3333 13.3333 150 579 Line 23.3333 12.6667 23.3333 13.3333 150 638 Line 22.6667 12.6667 23.3333 12.6667 150 773 Line 22 12.6667 22 13.3333 151 -1 Line 19.3333 0 20 0 151 202 Line 20 0 20 1.33333 152 187 Line 3.33333 3.33333 3.33333 4 152 390 Line 3.33333 4 4 4 152 551 Line 4 3.33333 4 4 152 686 Line 3.33333 2.66667 4 2.66667 152 730 Line 4 2.66667 4 3.33333 153 165 Line 5.33333 0.666667 8 0.666667 153 303 Line 8 0.666667 8 1.33333 153 761 Line 6 1.33333 6.66667 1.33333 154 179 Line 2.66667 18 4 18 154 231 Line 2.66667 18 2.66667 18.6667 155 -1 Line 4 22.6667 5.33333 22.6667 155 194 Line 5.33333 22 5.33333 22.6667 155 219 Line 4 22 4 22.6667 156 218 Line 22 16 22 16.6667 156 512 Line 22.6667 16 22.6667 16.6667 156 514 Line 22 18 22.6667 18 156 656 Line 22.6667 16.6667 22.6667 17.3333 156 657 Line 22.6667 17.3333 22.6667 18 157 -1 Line 0 14.6667 0 16 157 204 Line 0.666667 14.6667 0.666667 15.3333 157 319 Line 0 16 0.666667 16 157 475 Line 0 14.6667 0.666667 14.6667 158 236 Line 2.66667 10.6667 2.66667 12 158 325 Line 3.33333 10.6667 3.33333 11.3333 158 393 Line 2.66667 10.6667 3.33333 10.6667 158 805 Line 3.33333 11.3333 3.33333 12 158 952 Line 2.66667 12 3.33333 12 159 173 Line 1.33333 21.3333 1.33333 22 159 216 Line 2 20.6667 2 22 160 717 Line 29.3333 12.6667 29.3333 13.3333 160 968 Line 28 12.6667 28 13.3333 161 -1 Line 34 2.66667 34 3.33333 161 315 Line 30 3.33333 31.3333 3.33333 161 843 Line 29.3333 3.33333 30 3.33333 161 916 Line 29.3333 2.66667 29.3333 3.33333 162 198 Line 17.3333 2 17.3333 3.33333 162 254 Line 18.6667 1.33333 18.6667 4 162 722 Line 17.3333 3.33333 17.3333 4 162 808 Line 17.3333 4 18 4 162 809 Line 18 4 18.6667 4 162 957 Line 17.3333 1.33333 17.3333 2 163 260 Line 32 6.66667 32.6667 6.66667 163 642 Line 32.6667 6.66667 32.6667 7.33333 163 888 Line 32 8.66667 32.6667 8.66667 163 971 Line 32.6667 7.33333 32.6667 8 163 972 Line 32.6667 8 32.6667 8.66667 164 180 Line 5.33333 21.3333 6.66667 21.3333 164 194 Line 5.33333 22 7.33333 22 164 233 Line 7.33333 21.3333 7.33333 22 165 -1 Line 5.33333 0 10 0 165 303 Line 8 0.666667 10 0.666667 166 193 Line 22 22 24 22 166 271 Line 22 21.3333 24 21.3333 166 995 Line 24 21.3333 24 22 166 996 Line 22 21.3333 22 22 167 -1 Line 26 0 26.6667 0 167 705 Line 26 0 26 0.666667 167 706 Line 26 0.666667 26 1.33333 167 814 Line 26.6667 0.666667 26.6667 1.33333 167 961 Line 26 1.33333 26.6667 1.33333 168 210 Line 8.66667 15.3333 8.66667 16 168 309 Line 8.66667 14.6667 8.66667 15.3333 168 327 Line 8.66667 16 9.33333 16 168 340 Line 9.33333 15.3333 9.33333 16 168 900 Line 8.66667 14.6667 9.33333 14.6667 169 267 Line 13.3333 15.3333 14.6667 15.3333 169 450 Line 13.3333 14.6667 13.3333 15.3333 169 484 Line 13.3333 14.6667 14 14.6667 169 749 Line 14 14.6667 14.6667 14.6667 169 942 Line 14.6667 14.6667 14.6667 15.3333 170 260 Line 32.6667 5.33333 32.6667 6.66667 170 642 Line 32.6667 6.66667 33.3333 6.66667 170 840 Line 32.6667 5.33333 33.3333 5.33333 171 228 Line 30.6667 20.6667 30.6667 21.3333 171 247 Line 29.3333 20.6667 30 20.6667 171 276 Line 30 20.6667 30.6667 20.6667 172 480 Line 17.3333 8 17.3333 8.66667 172 481 Line 17.3333 8.66667 17.3333 9.33333 172 532 Line 17.3333 8 18 8 172 998 Line 17.3333 9.33333 18 9.33333 173 -1 Line 0 21.3333 0 22 174 259 Line 0.666667 12 0.666667 12.6667 174 293 Line 0.666667 12 1.33333 12 174 310 Line 2 12 2 12.6667 174 776 Line 0.666667 12.6667 1.33333 12.6667 174 777 Line 1.33333 12.6667 2 12.6667 174 813 Line 1.33333 12 2 12 175 768 Line 19.3333 8 20 8 175 810 Line 19.3333 8 19.3333 8.66667 175 973 Line 20 8.66667 20 9.33333 175 982 Line 19.3333 9.33333 20 9.33333 176 585 Line 5.33333 17.3333 6 17.3333 176 586 Line 6 17.3333 6.66667 17.3333 176 658 Line 6.66667 16.6667 6.66667 17.3333 176 990 Line 5.33333 16.6667 5.33333 17.3333 177 312 Line 28.6667 0.666667 28.6667 2 177 815 Line 28 0.666667 28 1.33333 178 203 Line 26 16 26 17.3333 178 225 Line 26 17.3333 26.6667 17.3333 178 264 Line 26.6667 16 26.6667 17.3333 178 313 Line 26 16 26.6667 16 179 230 Line 4 17.3333 4 18 179 511 Line 2.66667 17.3333 3.33333 17.3333 179 690 Line 2.66667 17.3333 2.66667 18 179 867 Line 3.33333 17.3333 4 17.3333 181 -1 Line 15.3333 0 16 0 181 223 Line 15.3333 0 15.3333 0.666667 181 240 Line 16 0 16 1.33333 181 709 Line 15.3333 1.33333 16 1.33333 181 736 Line 15.3333 0.666667 15.3333 1.33333 182 -1 Line 0 5.33333 0 6.66667 182 245 Line 0.666667 6.66667 2 6.66667 182 600 Line 1.33333 5.33333 2 5.33333 182 719 Line 0 6.66667 0.666667 6.66667 182 902 Line 2 5.33333 2 6 182 903 Line 2 6 2 6.66667 182 980 Line 0.666667 5.33333 1.33333 5.33333 183 -1 Line 34 8.66667 34 10.6667 183 392 Line 33.3333 10.6667 34 10.6667 183 414 Line 33.3333 8.66667 33.3333 9.33333 183 769 Line 33.3333 9.33333 33.3333 10 183 770 Line 33.3333 10 33.3333 10.6667 184 -1 Line 26.6667 22.6667 28.6667 22.6667 184 256 Line 26.6667 22 27.3333 22 184 304 Line 28.6667 22 28.6667 22.6667 184 565 Line 26.6667 22 26.6667 22.6667 185 741 Line 10 10.6667 10.6667 10.6667 185 774 Line 10 12 10.6667 12 185 999 Line 10.6667 11.3333 10.6667 12 186 288 Line 32.6667 16 32.6667 17.3333 186 892 Line 32.6667 16 33.3333 16 187 243 Line 2.66667 4 2.66667 4.66667 187 281 Line 2.66667 3.33333 2.66667 4 187 390 Line 3.33333 4 3.33333 4.66667 187 634 Line 2.66667 4.66667 3.33333 4.66667 188 -1 Line 22.6667 0 24.6667 0 188 307 Line 24 2.66667 24.6667 2.66667 188 314 Line 22.6667 0 22.6667 1.33333 188 643 Line 24.6667 0 24.6667 0.666667 188 943 Line 24.6667 0.666667 24.6667 1.33333 189 227 Line 20 6.66667 20.6667 6.66667 189 301 Line 20.6667 6.66667 22 6.66667 189 789 Line 22 6 22 6.66667 189 871 Line 20.6667 6 21.3333 6 189 880 Line 21.3333 6 22 6 189 970 Line 20 6 20.6667 6 190 196 Line 18 15.3333 18.6667 15.3333 190 263 Line 17.3333 15.3333 18 15.3333 190 385 Line 17.3333 14.6667 17.3333 15.3333 190 465 Line 17.3333 14.6667 18 14.6667 190 466 Line 18 14.6667 18.6667 14.6667 190 901 Line 18.6667 14.6667 18.6667 15.3333 191 -1 Line 34 18 34 19.3333 191 250 Line 33.3333 19.3333 34 19.3333 192 234 Line 10.6667 21.3333 10.6667 22 192 422 Line 12.6667 21.3333 12.6667 22 193 -1 Line 21.3333 22.6667 24.6667 22.6667 193 796 Line 21.3333 22 21.3333 22.6667 193 834 Line 24.6667 22 24.6667 22.6667 193 995 Line 24 22 24.6667 22 193 996 Line 21.3333 22 22 22 194 -1 Line 5.33333 22.6667 7.33333 22.6667 195 286 Line 4.66667 15.3333 4.66667 16 195 316 Line 3.33333 16 4.66667 16 195 467 Line 3.33333 15.3333 3.33333 16 196 253 Line 18 16 19.3333 16 196 263 Line 18 15.3333 18 16 196 901 Line 18.6667 15.3333 19.3333 15.3333 196 919 Line 19.3333 15.3333 19.3333 16 197 218 Line 20.6667 16 21.3333 16 197 628 Line 20.6667 14.6667 21.3333 14.6667 197 695 Line 21.3333 14.6667 21.3333 15.3333 197 696 Line 21.3333 15.3333 21.3333 16 197 944 Line 20.6667 14.6667 20.6667 15.3333 197 945 Line 20.6667 15.3333 20.6667 16 198 251 Line 16.6667 2 16.6667 2.66667 198 611 Line 16.6667 2.66667 16.6667 3.33333 198 722 Line 16.6667 3.33333 17.3333 3.33333 198 957 Line 16.6667 2 17.3333 2 199 223 Line 14 0.666667 14.6667 0.666667 199 342 Line 13.3333 0.666667 13.3333 1.33333 199 434 Line 13.3333 1.33333 14 1.33333 199 452 Line 13.3333 0.666667 14 0.666667 199 736 Line 14.6667 0.666667 14.6667 1.33333 199 771 Line 14 1.33333 14.6667 1.33333 200 367 Line 9.33333 6.66667 9.33333 7.33333 200 517 Line 9.33333 6.66667 10 6.66667 200 707 Line 10 6.66667 10.6667 6.66667 200 887 Line 10.6667 6.66667 10.6667 7.33333 200 926 Line 9.33333 7.33333 10 7.33333 200 927 Line 10 7.33333 10.6667 7.33333 201 205 Line 32.6667 13.3333 33.3333 13.3333 201 660 Line 32.6667 12 33.3333 12 201 923 Line 33.3333 12 33.3333 12.6667 201 924 Line 33.3333 12.6667 33.3333 13.3333 202 -1 Line 20 0 20.6667 0 202 837 Line 20 4 20.6667 4 202 941 Line 20.6667 3.33333 20.6667 4 202 948 Line 20 3.33333 20 4 203 225 Line 24.6667 17.3333 26 17.3333 203 273 Line 25.3333 16 26 16 203 430 Line 24.6667 16 24.6667 16.6667 203 864 Line 24.6667 16 25.3333 16 203 922 Line 24.6667 16.6667 24.6667 17.3333 204 737 Line 2.66667 14.6667 2.66667 15.3333 205 419 Line 33.3333 14 33.3333 14.6667 205 487 Line 33.3333 13.3333 33.3333 14 205 891 Line 32.6667 14.6667 33.3333 14.6667 206 236 Line 2 10.6667 2.66667 10.6667 206 393 Line 2.66667 10 2.66667 10.6667 206 425 Line 2.66667 9.33333 2.66667 10 206 597 Line 2 8.66667 2.66667 8.66667 206 699 Line 2.66667 8.66667 2.66667 9.33333 207 242 Line 28 17.3333 29.3333 17.3333 207 270 Line 28 16 28 17.3333 208 266 Line 27.3333 13.3333 27.3333 14.6667 208 763 Line 26.6667 14 26.6667 14.6667 208 967 Line 26.6667 13.3333 27.3333 13.3333 209 306 Line 23.3333 7.33333 24.6667 7.33333 209 732 Line 24.6667 6.66667 24.6667 7.33333 209 812 Line 22.6667 7.33333 23.3333 7.33333 209 934 Line 22.6667 6.66667 22.6667 7.33333 210 285 Line 7.33333 16 8.66667 16 210 309 Line 7.33333 15.3333 8.66667 15.3333 211 224 Line 32 19.3333 32 22 211 250 Line 33.3333 19.3333 33.3333 20.6667 211 827 Line 32 22 32.6667 22 211 828 Line 32.6667 22 33.3333 22 211 881 Line 33.3333 20.6667 33.3333 21.3333 211 882 Line 33.3333 21.3333 33.3333 22 212 225 Line 24.6667 18.6667 24.6667 20 212 317 Line 24 18.6667 24.6667 18.6667 212 869 Line 24 20 24.6667 20 213 279 Line 8.66667 2 10 2 213 482 Line 9.33333 2.66667 10 2.66667 213 523 Line 10 2 10 2.66667 213 788 Line 8.66667 2.66667 9.33333 2.66667 214 297 Line 31.3333 10.6667 32.6667 10.6667 214 652 Line 31.3333 10 32 10 214 653 Line 32 10 32.6667 10 214 770 Line 32.6667 10 32.6667 10.6667 214 920 Line 31.3333 10 31.3333 10.6667 215 234 Line 8.66667 21.3333 10 21.3333 216 981 Line 2 20.6667 2.66667 20.6667 217 308 Line 21.3333 4.66667 21.3333 5.33333 217 880 Line 21.3333 5.33333 22 5.33333 217 954 Line 21.3333 4.66667 22 4.66667 217 958 Line 22 5.33333 22.6667 5.33333 217 984 Line 22.6667 4.66667 22.6667 5.33333 218 696 Line 21.3333 16 22 16 218 697 Line 20.6667 16 20.6667 16.6667 219 -1 Line 2.66667 22.6667 4 22.6667 220 825 Line 28.6667 11.3333 28.6667 12 220 904 Line 28 10.6667 28.6667 10.6667 220 964 Line 28 10.6667 28 11.3333 221 294 Line 14 17.3333 14 18 221 343 Line 14 18.6667 14 19.3333 221 537 Line 13.3333 19.3333 14 19.3333 221 540 Line 14 18 14 18.6667 222 309 Line 7.33333 14.6667 8.66667 14.6667 222 378 Line 8 13.3333 8.66667 13.3333 222 729 Line 7.33333 13.3333 8 13.3333 222 899 Line 8.66667 13.3333 8.66667 14 222 900 Line 8.66667 14 8.66667 14.6667 223 -1 Line 14 0 15.3333 0 223 452 Line 14 0 14 0.666667 223 736 Line 14.6667 0.666667 15.3333 0.666667 224 228 Line 31.3333 19.3333 31.3333 21.3333 224 311 Line 31.3333 21.3333 31.3333 22 224 829 Line 31.3333 22 32 22 225 242 Line 28 17.3333 28 19.3333 225 257 Line 27.3333 20 28 20 225 264 Line 26.6667 17.3333 27.3333 17.3333 225 270 Line 27.3333 17.3333 28 17.3333 225 299 Line 24.6667 20 26 20 225 317 Line 24.6667 17.3333 24.6667 18.6667 226 267 Line 12.6667 16 12.6667 16.6667 226 282 Line 11.3333 16 11.3333 16.6667 226 365 Line 11.3333 16 12 16 226 366 Line 12 16 12.6667 16 227 301 Line 20.6667 6.66667 20.6667 7.33333 227 768 Line 20 7.33333 20 8 228 276 Line 30.6667 19.3333 30.6667 20.6667 228 311 Line 30.6667 21.3333 31.3333 21.3333 229 275 Line 31.3333 12 32 12 229 633 Line 30.6667 12 31.3333 12 230 585 Line 5.33333 17.3333 5.33333 18 230 868 Line 4 17.3333 4.66667 17.3333 230 990 Line 4.66667 17.3333 5.33333 17.3333 231 690 Line 2 18 2.66667 18 231 981 Line 2 20 2.66667 20 232 472 Line 14.6667 5.33333 15.3333 5.33333 232 494 Line 14.6667 4 15.3333 4 232 785 Line 14.6667 4 14.6667 4.66667 232 786 Line 14.6667 4.66667 14.6667 5.33333 233 234 Line 8.66667 21.3333 8.66667 22 235 417 Line 12 4.66667 12.6667 4.66667 235 510 Line 11.3333 4 11.3333 4.66667 235 582 Line 12.6667 4 12.6667 4.66667 235 965 Line 11.3333 4 12 4 235 966 Line 12 4 12.6667 4 235 989 Line 11.3333 4.66667 12 4.66667 236 310 Line 2 12 2.66667 12 236 813 Line 2 11.3333 2 12 237 479 Line 5.33333 12 5.33333 12.6667 237 701 Line 4 12.6667 4.66667 12.6667 237 806 Line 4 12 4 12.6667 238 253 Line 18 16 18 16.6667 238 263 Line 16.6667 16 18 16 238 296 Line 16.6667 16.6667 17.3333 16.6667 238 925 Line 16.6667 16 16.6667 16.6667 239 269 Line 4.66667 6.66667 6 6.66667 239 278 Line 5.33333 6 6 6 239 550 Line 4 6 4 6.66667 239 851 Line 4 6.66667 4.66667 6.66667 239 985 Line 6 6 6 6.66667 240 -1 Line 16 0 16.6667 0 240 710 Line 16 1.33333 16.6667 1.33333 240 723 Line 16.6667 0 16.6667 0.666667 240 724 Line 16.6667 0.666667 16.6667 1.33333 241 244 Line 1.33333 16.6667 2 16.6667 241 383 Line 2.66667 16 2.66667 16.6667 241 421 Line 2 16.6667 2.66667 16.6667 241 711 Line 1.33333 16 1.33333 16.6667 243 281 Line 2 4 2.66667 4 243 291 Line 2 4 2 4.66667 243 600 Line 2 4.66667 2 5.33333 243 634 Line 2.66667 4.66667 2.66667 5.33333 243 902 Line 2 5.33333 2.66667 5.33333 244 -1 Line 0 16.6667 0 17.3333 244 319 Line 0 16.6667 0.666667 16.6667 244 421 Line 2 16.6667 2 17.3333 244 711 Line 0.666667 16.6667 1.33333 16.6667 245 719 Line 0.666667 6.66667 0.666667 7.33333 245 807 Line 2 6.66667 2 7.33333 246 249 Line 21.3333 2.66667 22 2.66667 246 941 Line 21.3333 3.33333 21.3333 4 246 954 Line 21.3333 4 22 4 247 276 Line 30 19.3333 30 20.6667 248 287 Line 14 21.3333 15.3333 21.3333 248 331 Line 14 20 14 20.6667 248 443 Line 14 20.6667 14 21.3333 248 629 Line 14 20 14.6667 20 248 630 Line 14.6667 20 15.3333 20 249 -1 Line 21.3333 0 22 0 249 314 Line 22 0 22 1.33333 250 -1 Line 34 19.3333 34 20.6667 250 881 Line 33.3333 20.6667 34 20.6667 251 539 Line 15.3333 2 15.3333 2.66667 251 610 Line 15.3333 2.66667 16 2.66667 251 611 Line 16 2.66667 16.6667 2.66667 251 709 Line 15.3333 2 16 2 251 710 Line 16 2 16.6667 2 252 268 Line 3.33333 1.33333 4 1.33333 252 686 Line 3.33333 2 4 2 252 687 Line 4 2 4.66667 2 252 994 Line 4 1.33333 4.66667 1.33333 253 641 Line 19.3333 16 19.3333 16.6667 253 929 Line 18.6667 16.6667 19.3333 16.6667 254 948 Line 19.3333 3.33333 19.3333 4 255 295 Line 30.6667 8.66667 31.3333 8.66667 255 917 Line 29.3333 8.66667 29.3333 9.33333 256 565 Line 26 22 26.6667 22 256 833 Line 26 21.3333 26 22 258 280 Line 18.6667 19.3333 19.3333 19.3333 258 929 Line 18.6667 17.3333 19.3333 17.3333 259 -1 Line 0 12 0 13.3333 259 776 Line 0.666667 12.6667 0.666667 13.3333 260 302 Line 32 5.33333 32.6667 5.33333 261 -1 Line 0 0.666667 0 2 261 274 Line 1.33333 0.666667 1.33333 1.33333 261 598 Line 0 2 0.666667 2 261 599 Line 0.666667 2 1.33333 2 262 476 Line 26.6667 10 27.3333 10 262 930 Line 26.6667 10.6667 26.6667 11.3333 262 963 Line 27.3333 10 27.3333 10.6667 262 964 Line 27.3333 10.6667 27.3333 11.3333 263 385 Line 16.6667 15.3333 17.3333 15.3333 263 399 Line 16.6667 15.3333 16.6667 16 264 270 Line 27.3333 16 27.3333 17.3333 265 330 Line 22 12 22.6667 12 265 410 Line 21.3333 12 22 12 265 572 Line 21.3333 12 21.3333 12.6667 265 638 Line 22.6667 12 22.6667 12.6667 265 773 Line 21.3333 12.6667 22 12.6667 266 968 Line 27.3333 13.3333 28 13.3333 267 294 Line 14 16.6667 14.6667 16.6667 267 338 Line 15.3333 16 15.3333 16.6667 267 355 Line 14.6667 16.6667 15.3333 16.6667 267 366 Line 12.6667 15.3333 12.6667 16 267 450 Line 12.6667 15.3333 13.3333 15.3333 267 910 Line 15.3333 15.3333 15.3333 16 267 942 Line 14.6667 15.3333 15.3333 15.3333 268 -1 Line 3.33333 0 4 0 268 624 Line 3.33333 0 3.33333 0.666667 268 993 Line 4 0 4 0.666667 268 994 Line 4 0.666667 4 1.33333 269 851 Line 4.66667 6.66667 4.66667 7.33333 271 299 Line 24.6667 20.6667 24.6667 21.3333 271 869 Line 24 20.6667 24.6667 20.6667 271 873 Line 22 20.6667 22.6667 20.6667 271 995 Line 24 21.3333 24.6667 21.3333 272 472 Line 14.6667 6 15.3333 6 272 541 Line 14.6667 6 14.6667 6.66667 272 607 Line 15.3333 6 15.3333 6.66667 272 874 Line 15.3333 6.66667 15.3333 7.33333 273 313 Line 26 14.6667 26 16 273 765 Line 25.3333 14.6667 26 14.6667 273 864 Line 25.3333 15.3333 25.3333 16 273 866 Line 25.3333 14.6667 25.3333 15.3333 275 297 Line 31.3333 11.3333 32.6667 11.3333 275 633 Line 31.3333 11.3333 31.3333 12 275 660 Line 32.6667 11.3333 32.6667 12 277 361 Line 26 6 26 6.66667 277 732 Line 24.6667 6.66667 25.3333 6.66667 277 733 Line 25.3333 6.66667 26 6.66667 278 373 Line 6.66667 5.33333 6.66667 6 278 782 Line 5.33333 5.33333 6 5.33333 278 783 Line 6 5.33333 6.66667 5.33333 278 985 Line 6 6 6.66667 6 279 303 Line 8.66667 1.33333 10 1.33333 279 523 Line 10 2 10.6667 2 279 742 Line 10.6667 1.33333 10.6667 2 280 950 Line 18.6667 20.6667 19.3333 20.6667 281 291 Line 2 3.33333 2 4 281 636 Line 2 2.66667 2 3.33333 282 544 Line 10 16 10.6667 16 282 545 Line 10.6667 16 11.3333 16 282 797 Line 10 16 10 16.6667 283 463 Line 17.3333 11.3333 17.3333 12 283 702 Line 16 11.3333 16.6667 11.3333 283 703 Line 16.6667 11.3333 17.3333 11.3333 283 913 Line 16 12 16.6667 12 283 914 Line 16.6667 12 17.3333 12 284 553 Line 13.3333 11.3333 14 11.3333 284 556 Line 14 10 14 10.6667 284 557 Line 14 10.6667 14 11.3333 284 762 Line 13.3333 10 14 10 284 947 Line 13.3333 10 13.3333 10.6667 285 327 Line 8.66667 16 8.66667 16.6667 285 647 Line 7.33333 16.6667 8 16.6667 285 648 Line 8 16.6667 8.66667 16.6667 285 792 Line 7.33333 16 7.33333 16.6667 286 529 Line 4.66667 16 5.33333 16 287 -1 Line 14 22.6667 15.3333 22.6667 288 870 Line 32 16 32.6667 16 289 463 Line 17.3333 11.3333 18 11.3333 289 640 Line 17.3333 10 17.3333 10.6667 289 703 Line 17.3333 10.6667 17.3333 11.3333 289 998 Line 17.3333 10 18 10 290 -1 Line 16.6667 22.6667 17.3333 22.6667 291 600 Line 1.33333 4.66667 2 4.66667 291 636 Line 1.33333 3.33333 2 3.33333 291 978 Line 1.33333 3.33333 1.33333 4 291 979 Line 1.33333 4 1.33333 4.66667 292 614 Line 2.66667 13.3333 2.66667 14 292 737 Line 2.66667 14.6667 3.33333 14.6667 292 953 Line 2.66667 13.3333 3.33333 13.3333 293 813 Line 1.33333 11.3333 1.33333 12 293 992 Line 0.666667 10.6667 1.33333 10.6667 294 355 Line 14.6667 16.6667 14.6667 17.3333 294 356 Line 14.6667 17.3333 14.6667 18 294 540 Line 14 18 14.6667 18 295 623 Line 30.6667 7.33333 30.6667 8 296 505 Line 16 16.6667 16 17.3333 296 925 Line 16 16.6667 16.6667 16.6667 297 659 Line 32.6667 10.6667 32.6667 11.3333 297 921 Line 31.3333 10.6667 31.3333 11.3333 298 507 Line 28 4 28 4.66667 298 508 Line 28 4.66667 28 5.33333 298 564 Line 28 5.33333 28.6667 5.33333 298 818 Line 28 4 28.6667 4 299 832 Line 24.6667 21.3333 25.3333 21.3333 299 833 Line 25.3333 21.3333 26 21.3333 299 869 Line 24.6667 20 24.6667 20.6667 300 768 Line 19.3333 7.33333 19.3333 8 300 810 Line 18.6667 8 19.3333 8 300 896 Line 18.6667 7.33333 18.6667 8 300 897 Line 18.6667 6 18.6667 6.66667 300 898 Line 18.6667 6.66667 18.6667 7.33333 301 934 Line 22 6.66667 22 7.33333 302 604 Line 31.3333 4.66667 31.3333 5.33333 302 840 Line 32.6667 4.66667 32.6667 5.33333 302 854 Line 31.3333 4.66667 32 4.66667 302 855 Line 32 4.66667 32.6667 4.66667 304 -1 Line 28.6667 22.6667 29.3333 22.6667 305 856 Line 23.3333 5.33333 24 5.33333 305 958 Line 22.6667 5.33333 22.6667 6 305 984 Line 22.6667 5.33333 23.3333 5.33333 306 336 Line 24 8 24.6667 8 306 350 Line 24.6667 7.33333 24.6667 8 306 682 Line 23.3333 8 24 8 306 812 Line 23.3333 7.33333 23.3333 8 307 760 Line 24.6667 2.66667 24.6667 3.33333 307 799 Line 24.6667 3.33333 24.6667 4 307 858 Line 24 4 24.6667 4 308 837 Line 20.6667 4 20.6667 4.66667 308 838 Line 20.6667 4.66667 20.6667 5.33333 308 871 Line 20.6667 5.33333 21.3333 5.33333 308 941 Line 20.6667 4 21.3333 4 308 954 Line 21.3333 4 21.3333 4.66667 310 614 Line 2 13.3333 2.66667 13.3333 310 777 Line 2 12.6667 2 13.3333 310 952 Line 2.66667 12 2.66667 12.6667 310 953 Line 2.66667 12.6667 2.66667 13.3333 311 -1 Line 30.6667 22.6667 31.3333 22.6667 311 829 Line 31.3333 22 31.3333 22.6667 312 631 Line 28.6667 0.666667 29.3333 0.666667 312 915 Line 28.6667 2 29.3333 2 313 763 Line 26 14.6667 26.6667 14.6667 314 -1 Line 22 0 22.6667 0 315 603 Line 30.6667 4 31.3333 4 315 843 Line 30 3.33333 30 4 315 883 Line 30 4 30.6667 4 316 383 Line 3.33333 16 3.33333 16.6667 316 529 Line 4.66667 16 4.66667 16.6667 316 867 Line 3.33333 16.6667 4 16.6667 316 868 Line 4 16.6667 4.66667 16.6667 317 478 Line 24 18 24 18.6667 317 513 Line 24 17.3333 24 18 317 922 Line 24 17.3333 24.6667 17.3333 318 353 Line 8 12 8 12.6667 318 378 Line 8 12.6667 8.66667 12.6667 318 975 Line 8 12 8.66667 12 319 -1 Line 0 16 0 16.6667 319 711 Line 0.666667 16 0.666667 16.6667 320 344 Line 14.6667 18.6667 15.3333 18.6667 320 356 Line 14.6667 18 15.3333 18 320 540 Line 14.6667 18 14.6667 18.6667 321 378 Line 8.66667 12.6667 8.66667 13.3333 321 899 Line 8.66667 13.3333 9.33333 13.3333 322 453 Line 20 12 20 12.6667 322 572 Line 20.6667 12 20.6667 12.6667 322 669 Line 20 12.6667 20.6667 12.6667 323 327 Line 8.66667 16.6667 9.33333 16.6667 323 648 Line 8.66667 16.6667 8.66667 17.3333 324 400 Line 12.6667 7.33333 12.6667 8 324 438 Line 13.3333 7.33333 13.3333 8 324 781 Line 12.6667 8 13.3333 8 325 548 Line 3.33333 10.6667 4 10.6667 325 574 Line 4 10.6667 4 11.3333 325 805 Line 3.33333 11.3333 4 11.3333 326 411 Line 16 13.3333 16 14 326 444 Line 15.3333 14 16 14 326 561 Line 15.3333 13.3333 15.3333 14 327 797 Line 9.33333 16 9.33333 16.6667 328 451 Line 27.3333 5.33333 27.3333 6 328 508 Line 27.3333 5.33333 28 5.33333 328 564 Line 28 5.33333 28 6 329 330 Line 22 11.3333 22.6667 11.3333 329 409 Line 22 10.6667 22 11.3333 329 474 Line 22 10.6667 22.6667 10.6667 330 410 Line 22 11.3333 22 12 331 443 Line 13.3333 20.6667 14 20.6667 331 537 Line 13.3333 20 14 20 332 397 Line 5.33333 3.33333 6 3.33333 332 680 Line 6 2.66667 6 3.33333 332 685 Line 5.33333 2.66667 6 2.66667 332 731 Line 5.33333 2.66667 5.33333 3.33333 333 439 Line 22.6667 10 23.3333 10 333 470 Line 23.3333 10 23.3333 10.6667 333 474 Line 22.6667 10 22.6667 10.6667 334 391 Line 4.66667 4 4.66667 4.66667 334 486 Line 4.66667 4.66667 5.33333 4.66667 334 552 Line 4.66667 4 5.33333 4 334 801 Line 5.33333 4 5.33333 4.66667 335 358 Line 19.3333 11.3333 19.3333 12 335 406 Line 18.6667 12 19.3333 12 335 464 Line 18.6667 11.3333 18.6667 12 335 794 Line 18.6667 11.3333 19.3333 11.3333 336 337 Line 24 8.66667 24.6667 8.66667 336 436 Line 24.6667 8 24.6667 8.66667 336 682 Line 24 8 24 8.66667 337 379 Line 24.6667 8.66667 24.6667 9.33333 337 395 Line 24 9.33333 24.6667 9.33333 337 683 Line 24 8.66667 24 9.33333 338 505 Line 15.3333 16.6667 16 16.6667 338 910 Line 15.3333 16 16 16 338 925 Line 16 16 16 16.6667 339 394 Line 10.6667 14.6667 11.3333 14.6667 339 605 Line 11.3333 14 11.3333 14.6667 339 956 Line 10.6667 14 10.6667 14.6667 340 544 Line 10 15.3333 10 16 340 797 Line 9.33333 16 10 16 341 -1 Line 12.6667 0 13.3333 0 341 342 Line 12.6667 0.666667 13.3333 0.666667 341 452 Line 13.3333 0 13.3333 0.666667 341 716 Line 12.6667 0 12.6667 0.666667 342 403 Line 12.6667 0.666667 12.6667 1.33333 342 433 Line 12.6667 1.33333 13.3333 1.33333 343 344 Line 14.6667 18.6667 14.6667 19.3333 343 540 Line 14 18.6667 14.6667 18.6667 343 629 Line 14 19.3333 14.6667 19.3333 344 630 Line 14.6667 19.3333 15.3333 19.3333 345 346 Line 16.6667 14 16.6667 14.6667 345 376 Line 16 14.6667 16.6667 14.6667 345 411 Line 16 14 16.6667 14 345 444 Line 16 14 16 14.6667 346 385 Line 16.6667 14.6667 17.3333 14.6667 346 412 Line 16.6667 14 17.3333 14 346 465 Line 17.3333 14 17.3333 14.6667 347 348 Line 8.66667 6 9.33333 6 347 407 Line 8.66667 5.33333 9.33333 5.33333 347 424 Line 9.33333 5.33333 9.33333 6 347 432 Line 8.66667 5.33333 8.66667 6 348 367 Line 8.66667 6.66667 9.33333 6.66667 348 515 Line 8.66667 6 8.66667 6.66667 348 517 Line 9.33333 6 9.33333 6.66667 349 416 Line 14 12 14 12.6667 349 553 Line 13.3333 12 14 12 349 562 Line 13.3333 12.6667 14 12.6667 349 609 Line 13.3333 12 13.3333 12.6667 350 351 Line 25.3333 7.33333 25.3333 8 350 436 Line 24.6667 8 25.3333 8 350 732 Line 24.6667 7.33333 25.3333 7.33333 351 375 Line 26 7.33333 26 8 351 437 Line 25.3333 8 26 8 351 733 Line 25.3333 7.33333 26 7.33333 352 384 Line 16.6667 7.33333 16.6667 8 352 480 Line 16.6667 8 17.3333 8 352 532 Line 17.3333 7.33333 17.3333 8 352 746 Line 16.6667 7.33333 17.3333 7.33333 353 519 Line 7.33333 12 7.33333 12.6667 353 729 Line 7.33333 12.6667 8 12.6667 354 502 Line 12 12.6667 12 13.3333 354 559 Line 12.6667 12.6667 12.6667 13.3333 354 608 Line 12 12.6667 12.6667 12.6667 354 811 Line 12 13.3333 12.6667 13.3333 355 356 Line 14.6667 17.3333 15.3333 17.3333 355 505 Line 15.3333 16.6667 15.3333 17.3333 357 358 Line 19.3333 11.3333 20 11.3333 357 458 Line 20 10.6667 20 11.3333 357 794 Line 19.3333 10.6667 19.3333 11.3333 357 983 Line 19.3333 10.6667 20 10.6667 358 453 Line 19.3333 12 20 12 359 596 Line 2.66667 7.33333 2.66667 8 359 635 Line 2.66667 7.33333 3.33333 7.33333 359 698 Line 2.66667 8 3.33333 8 359 734 Line 3.33333 7.33333 3.33333 8 360 442 Line 14.6667 9.33333 15.3333 9.33333 360 718 Line 14.6667 8.66667 14.6667 9.33333 360 906 Line 15.3333 8.66667 15.3333 9.33333 361 374 Line 26 6.66667 26.6667 6.66667 361 428 Line 26.6667 6 26.6667 6.66667 362 363 Line 27.3333 7.33333 27.3333 8 362 375 Line 26.6667 7.33333 26.6667 8 362 429 Line 26.6667 7.33333 27.3333 7.33333 362 691 Line 26.6667 8 27.3333 8 363 844 Line 28 7.33333 28 8 364 404 Line 8 10 8.66667 10 364 462 Line 8 10.6667 8.66667 10.6667 364 531 Line 8 10 8 10.6667 364 664 Line 8.66667 10 8.66667 10.6667 365 366 Line 12 15.3333 12 16 365 500 Line 11.3333 15.3333 12 15.3333 365 545 Line 11.3333 15.3333 11.3333 16 366 501 Line 12 15.3333 12.6667 15.3333 367 528 Line 8.66667 6.66667 8.66667 7.33333 367 651 Line 8.66667 7.33333 9.33333 7.33333 368 482 Line 10 2.66667 10 3.33333 368 489 Line 10.6667 2.66667 10.6667 3.33333 368 523 Line 10 2.66667 10.6667 2.66667 368 566 Line 10 3.33333 10.6667 3.33333 369 527 Line 7.33333 6.66667 7.33333 7.33333 369 533 Line 6.66667 6.66667 7.33333 6.66667 369 665 Line 6.66667 7.33333 7.33333 7.33333 370 390 Line 3.33333 4.66667 4 4.66667 370 485 Line 4 4.66667 4 5.33333 370 549 Line 3.33333 5.33333 4 5.33333 370 634 Line 3.33333 4.66667 3.33333 5.33333 371 447 Line 19.3333 13.3333 19.3333 14 371 454 Line 19.3333 13.3333 20 13.3333 371 670 Line 20 13.3333 20 14 372 405 Line 18 12 18 12.6667 372 463 Line 17.3333 12 18 12 372 577 Line 17.3333 12.6667 18 12.6667 372 914 Line 17.3333 12 17.3333 12.6667 373 431 Line 7.33333 5.33333 7.33333 6 373 533 Line 6.66667 6 7.33333 6 373 563 Line 6.66667 5.33333 7.33333 5.33333 374 375 Line 26 7.33333 26.6667 7.33333 374 429 Line 26.6667 6.66667 26.6667 7.33333 374 733 Line 26 6.66667 26 7.33333 375 593 Line 26 8 26.6667 8 376 385 Line 16.6667 14.6667 16.6667 15.3333 376 399 Line 16 15.3333 16.6667 15.3333 376 909 Line 16 14.6667 16 15.3333 377 435 Line 5.33333 11.3333 6 11.3333 377 490 Line 6 10.6667 6 11.3333 377 575 Line 5.33333 10.6667 5.33333 11.3333 377 758 Line 5.33333 10.6667 6 10.6667 378 729 Line 8 12.6667 8 13.3333 379 380 Line 25.3333 8.66667 25.3333 9.33333 379 436 Line 24.6667 8.66667 25.3333 8.66667 380 437 Line 25.3333 8.66667 26 8.66667 380 594 Line 26 8.66667 26 9.33333 381 382 Line 14.6667 10.6667 15.3333 10.6667 381 442 Line 14.6667 10 15.3333 10 381 556 Line 14.6667 10 14.6667 10.6667 381 862 Line 15.3333 10 15.3333 10.6667 382 426 Line 15.3333 10.6667 15.3333 11.3333 382 557 Line 14.6667 10.6667 14.6667 11.3333 382 738 Line 14.6667 11.3333 15.3333 11.3333 383 467 Line 2.66667 16 3.33333 16 383 511 Line 2.66667 16.6667 3.33333 16.6667 384 846 Line 16 8 16.6667 8 384 875 Line 16 7.33333 16 8 386 387 Line 7.33333 9.33333 7.33333 10 386 530 Line 6.66667 10 7.33333 10 386 589 Line 6.66667 9.33333 7.33333 9.33333 386 617 Line 6.66667 9.33333 6.66667 10 387 404 Line 8 9.33333 8 10 387 531 Line 7.33333 10 8 10 387 590 Line 7.33333 9.33333 8 9.33333 388 389 Line 16.6667 12.6667 16.6667 13.3333 388 411 Line 16 13.3333 16.6667 13.3333 388 913 Line 16 12.6667 16.6667 12.6667 389 412 Line 16.6667 13.3333 17.3333 13.3333 389 577 Line 17.3333 12.6667 17.3333 13.3333 389 914 Line 16.6667 12.6667 17.3333 12.6667 390 391 Line 4 4 4 4.66667 391 485 Line 4 4.66667 4.66667 4.66667 391 551 Line 4 4 4.66667 4 392 -1 Line 34 10.6667 34 11.3333 392 659 Line 33.3333 10.6667 33.3333 11.3333 392 908 Line 33.3333 11.3333 34 11.3333 393 425 Line 2.66667 10 3.33333 10 393 548 Line 3.33333 10 3.33333 10.6667 394 500 Line 11.3333 14.6667 11.3333 15.3333 394 545 Line 10.6667 15.3333 11.3333 15.3333 395 440 Line 24 9.33333 24 10 395 471 Line 24 10 24.6667 10 396 427 Line 13.3333 2.66667 13.3333 3.33333 396 555 Line 12.6667 2.66667 12.6667 3.33333 396 570 Line 12.6667 2.66667 13.3333 2.66667 396 581 Line 12.6667 3.33333 13.3333 3.33333 397 398 Line 6 3.33333 6 4 397 552 Line 5.33333 3.33333 5.33333 4 397 801 Line 5.33333 4 6 4 398 680 Line 6 3.33333 6.66667 3.33333 398 802 Line 6 4 6.66667 4 398 830 Line 6.66667 3.33333 6.66667 4 399 910 Line 16 15.3333 16 16 399 925 Line 16 16 16.6667 16 400 401 Line 12 7.33333 12.6667 7.33333 400 755 Line 12 7.33333 12 8 400 780 Line 12 8 12.6667 8 401 525 Line 12 6.66667 12.6667 6.66667 401 756 Line 12 6.66667 12 7.33333 402 403 Line 12 0.666667 12 1.33333 402 688 Line 11.3333 1.33333 12 1.33333 402 715 Line 11.3333 0.666667 12 0.666667 403 689 Line 12 1.33333 12.6667 1.33333 403 716 Line 12 0.666667 12.6667 0.666667 404 457 Line 8.66667 9.33333 8.66667 10 404 860 Line 8 9.33333 8.66667 9.33333 405 406 Line 18.6667 12 18.6667 12.6667 405 464 Line 18 12 18.6667 12 406 446 Line 18.6667 12.6667 19.3333 12.6667 406 453 Line 19.3333 12 19.3333 12.6667 407 526 Line 9.33333 4.66667 9.33333 5.33333 407 588 Line 8.66667 4.66667 8.66667 5.33333 407 646 Line 8.66667 4.66667 9.33333 4.66667 408 535 Line 4 8.66667 4 9.33333 408 547 Line 3.33333 9.33333 4 9.33333 408 699 Line 3.33333 8.66667 3.33333 9.33333 408 735 Line 3.33333 8.66667 4 8.66667 409 410 Line 21.3333 11.3333 22 11.3333 409 459 Line 21.3333 10.6667 21.3333 11.3333 409 473 Line 21.3333 10.6667 22 10.6667 411 412 Line 16.6667 13.3333 16.6667 14 412 578 Line 17.3333 13.3333 17.3333 14 413 550 Line 3.33333 6.66667 4 6.66667 413 635 Line 3.33333 6.66667 3.33333 7.33333 413 734 Line 3.33333 7.33333 4 7.33333 413 851 Line 4 6.66667 4 7.33333 414 769 Line 32.6667 9.33333 33.3333 9.33333 414 888 Line 32.6667 8.66667 32.6667 9.33333 414 972 Line 32.6667 8.66667 33.3333 8.66667 415 416 Line 14 12 14.6667 12 415 553 Line 14 11.3333 14 12 415 557 Line 14 11.3333 14.6667 11.3333 415 738 Line 14.6667 11.3333 14.6667 12 416 568 Line 14 12.6667 14.6667 12.6667 416 739 Line 14.6667 12 14.6667 12.6667 417 418 Line 12.6667 4.66667 12.6667 5.33333 417 524 Line 12 5.33333 12.6667 5.33333 417 989 Line 12 4.66667 12 5.33333 418 582 Line 12.6667 4.66667 13.3333 4.66667 418 714 Line 13.3333 4.66667 13.3333 5.33333 419 -1 Line 34 14 34 14.6667 419 487 Line 33.3333 14 34 14 419 911 Line 33.3333 14.6667 34 14.6667 420 472 Line 15.3333 5.33333 15.3333 6 420 607 Line 15.3333 6 16 6 420 876 Line 16 5.33333 16 6 421 511 Line 2.66667 16.6667 2.66667 17.3333 421 690 Line 2 17.3333 2.66667 17.3333 422 423 Line 12.6667 22 13.3333 22 423 -1 Line 12.6667 22.6667 13.3333 22.6667 424 517 Line 9.33333 6 10 6 424 526 Line 9.33333 5.33333 10 5.33333 424 595 Line 10 5.33333 10 6 425 547 Line 3.33333 9.33333 3.33333 10 425 699 Line 2.66667 9.33333 3.33333 9.33333 426 702 Line 16 10.6667 16 11.3333 426 862 Line 15.3333 10.6667 16 10.6667 427 571 Line 13.3333 2.66667 14 2.66667 427 753 Line 13.3333 3.33333 14 3.33333 427 766 Line 14 2.66667 14 3.33333 428 429 Line 26.6667 6.66667 27.3333 6.66667 428 451 Line 26.6667 6 27.3333 6 430 693 Line 24 16 24 16.6667 430 863 Line 24 16 24.6667 16 430 922 Line 24 16.6667 24.6667 16.6667 431 432 Line 8 5.33333 8 6 431 534 Line 7.33333 6 8 6 431 587 Line 7.33333 5.33333 8 5.33333 432 515 Line 8 6 8.66667 6 432 588 Line 8 5.33333 8.66667 5.33333 433 434 Line 13.3333 1.33333 13.3333 2 433 570 Line 12.6667 2 13.3333 2 433 689 Line 12.6667 1.33333 12.6667 2 434 571 Line 13.3333 2 14 2 434 771 Line 14 1.33333 14 2 435 461 Line 6 11.3333 6 12 435 479 Line 5.33333 12 6 12 436 437 Line 25.3333 8 25.3333 8.66667 437 593 Line 26 8 26 8.66667 438 620 Line 13.3333 8 14 8 438 661 Line 13.3333 7.33333 14 7.33333 439 440 Line 23.3333 9.33333 23.3333 10 439 469 Line 22.6667 9.33333 22.6667 10 440 470 Line 23.3333 10 24 10 440 683 Line 23.3333 9.33333 24 9.33333 441 442 Line 14.6667 9.33333 14.6667 10 441 556 Line 14 10 14.6667 10 441 718 Line 14 9.33333 14.6667 9.33333 441 762 Line 14 9.33333 14 10 442 861 Line 15.3333 9.33333 15.3333 10 444 750 Line 15.3333 14 15.3333 14.6667 444 909 Line 15.3333 14.6667 16 14.6667 445 555 Line 12 2.66667 12.6667 2.66667 445 570 Line 12.6667 2 12.6667 2.66667 445 689 Line 12 2 12.6667 2 446 447 Line 18.6667 13.3333 19.3333 13.3333 446 454 Line 19.3333 12.6667 19.3333 13.3333 448 449 Line 6.66667 2 6.66667 2.66667 448 680 Line 6 2.66667 6.66667 2.66667 448 685 Line 6 2 6 2.66667 448 761 Line 6 2 6.66667 2 449 681 Line 6.66667 2.66667 7.33333 2.66667 450 483 Line 12.6667 14.6667 13.3333 14.6667 450 501 Line 12.6667 14.6667 12.6667 15.3333 452 -1 Line 13.3333 0 14 0 453 454 Line 19.3333 12.6667 20 12.6667 454 669 Line 20 12.6667 20 13.3333 455 456 Line 20.6667 10 21.3333 10 455 468 Line 21.3333 9.33333 21.3333 10 455 778 Line 20.6667 9.33333 20.6667 10 455 974 Line 20.6667 9.33333 21.3333 9.33333 456 459 Line 20.6667 10.6667 21.3333 10.6667 456 473 Line 21.3333 10 21.3333 10.6667 456 779 Line 20.6667 10 20.6667 10.6667 457 546 Line 9.33333 9.33333 9.33333 10 457 664 Line 8.66667 10 9.33333 10 457 744 Line 8.66667 9.33333 9.33333 9.33333 458 459 Line 20.6667 10.6667 20.6667 11.3333 458 779 Line 20 10.6667 20.6667 10.6667 460 525 Line 12 6 12 6.66667 460 708 Line 11.3333 6 11.3333 6.66667 460 713 Line 11.3333 6 12 6 460 756 Line 11.3333 6.66667 12 6.66667 461 490 Line 6 11.3333 6.66667 11.3333 461 518 Line 6 12 6.66667 12 462 824 Line 8 10.6667 8 11.3333 462 960 Line 8.66667 10.6667 8.66667 11.3333 462 975 Line 8 11.3333 8.66667 11.3333 463 464 Line 18 11.3333 18 12 465 466 Line 18 14 18 14.6667 465 578 Line 17.3333 14 18 14 467 737 Line 2.66667 15.3333 3.33333 15.3333 468 469 Line 22 9.33333 22 10 468 473 Line 21.3333 10 22 10 469 474 Line 22 10 22.6667 10 470 471 Line 24 10 24 10.6667 471 878 Line 24 10.6667 24.6667 10.6667 472 506 Line 14.6667 5.33333 14.6667 6 473 474 Line 22 10 22 10.6667 475 -1 Line 0 14 0 14.6667 476 692 Line 26.6667 9.33333 27.3333 9.33333 477 478 Line 23.3333 18 23.3333 18.6667 477 514 Line 22.6667 18 22.6667 18.6667 477 657 Line 22.6667 18 23.3333 18 478 513 Line 23.3333 18 24 18 479 518 Line 6 12 6 12.6667 480 481 Line 16.6667 8.66667 17.3333 8.66667 480 846 Line 16.6667 8 16.6667 8.66667 481 639 Line 16.6667 9.33333 17.3333 9.33333 481 847 Line 16.6667 8.66667 16.6667 9.33333 482 625 Line 9.33333 3.33333 10 3.33333 482 788 Line 9.33333 2.66667 9.33333 3.33333 483 484 Line 13.3333 14 13.3333 14.6667 483 560 Line 12.6667 14 13.3333 14 483 606 Line 12.6667 14 12.6667 14.6667 484 591 Line 13.3333 14 14 14 484 749 Line 14 14 14 14.6667 485 486 Line 4.66667 4.66667 4.66667 5.33333 486 782 Line 5.33333 4.66667 5.33333 5.33333 487 -1 Line 34 13.3333 34 14 487 924 Line 33.3333 13.3333 34 13.3333 488 751 Line 17.3333 6 18 6 488 877 Line 17.3333 5.33333 17.3333 6 489 554 Line 11.3333 2.66667 11.3333 3.33333 489 567 Line 10.6667 3.33333 11.3333 3.33333 490 613 Line 6 10.6667 6.66667 10.6667 490 823 Line 6.66667 10.6667 6.66667 11.3333 491 492 Line 22.6667 14 23.3333 14 491 580 Line 23.3333 13.3333 23.3333 14 492 678 Line 23.3333 14 23.3333 14.6667 492 849 Line 22.6667 14.6667 23.3333 14.6667 493 494 Line 14.6667 3.33333 15.3333 3.33333 493 539 Line 14.6667 2.66667 15.3333 2.66667 493 610 Line 15.3333 2.66667 15.3333 3.33333 493 766 Line 14.6667 2.66667 14.6667 3.33333 494 767 Line 14.6667 3.33333 14.6667 4 495 496 Line 28 6.66667 28.6667 6.66667 495 520 Line 28.6667 6 28.6667 6.66667 495 564 Line 28 6 28.6667 6 496 521 Line 28.6667 6.66667 28.6667 7.33333 496 844 Line 28 7.33333 28.6667 7.33333 497 516 Line 28.6667 8 29.3333 8 497 521 Line 28.6667 7.33333 29.3333 7.33333 497 622 Line 29.3333 7.33333 29.3333 8 497 844 Line 28.6667 7.33333 28.6667 8 498 774 Line 10 12 10 12.6667 499 649 Line 24.6667 11.3333 25.3333 11.3333 499 878 Line 24.6667 10.6667 24.6667 11.3333 499 932 Line 25.3333 10.6667 25.3333 11.3333 500 501 Line 12 14.6667 12 15.3333 500 605 Line 11.3333 14.6667 12 14.6667 501 606 Line 12 14.6667 12.6667 14.6667 503 504 Line 16 10 16.6667 10 503 639 Line 16.6667 9.33333 16.6667 10 503 847 Line 16 9.33333 16.6667 9.33333 503 861 Line 16 9.33333 16 10 504 640 Line 16.6667 10 16.6667 10.6667 504 702 Line 16 10.6667 16.6667 10.6667 504 862 Line 16 10 16 10.6667 506 541 Line 14 6 14.6667 6 506 786 Line 14 5.33333 14.6667 5.33333 507 508 Line 27.3333 4.66667 28 4.66667 509 510 Line 10.6667 4 10.6667 4.66667 509 566 Line 10 4 10.6667 4 509 626 Line 10 4 10 4.66667 509 976 Line 10 4.66667 10.6667 4.66667 510 567 Line 10.6667 4 11.3333 4 510 988 Line 10.6667 4.66667 11.3333 4.66667 511 867 Line 3.33333 16.6667 3.33333 17.3333 512 656 Line 22.6667 16.6667 23.3333 16.6667 512 693 Line 23.3333 16 23.3333 16.6667 512 850 Line 22.6667 16 23.3333 16 513 657 Line 23.3333 17.3333 23.3333 18 513 694 Line 23.3333 17.3333 24 17.3333 514 558 Line 22 18.6667 22.6667 18.6667 515 528 Line 8 6.66667 8.66667 6.66667 515 534 Line 8 6 8 6.66667 516 845 Line 28.6667 8 28.6667 8.66667 516 917 Line 28.6667 8.66667 29.3333 8.66667 517 707 Line 10 6 10 6.66667 518 519 Line 6.66667 12 6.66667 12.6667 518 727 Line 6 12.6667 6.66667 12.6667 519 728 Line 6.66667 12.6667 7.33333 12.6667 520 521 Line 28.6667 6.66667 29.3333 6.66667 522 681 Line 7.33333 2.66667 7.33333 3.33333 522 787 Line 8 2.66667 8 3.33333 522 816 Line 7.33333 3.33333 8 3.33333 524 525 Line 12 6 12.6667 6 524 713 Line 12 5.33333 12 6 526 626 Line 9.33333 4.66667 10 4.66667 526 976 Line 10 4.66667 10 5.33333 527 528 Line 8 6.66667 8 7.33333 527 534 Line 7.33333 6.66667 8 6.66667 527 666 Line 7.33333 7.33333 8 7.33333 528 671 Line 8 7.33333 8.66667 7.33333 529 990 Line 4.66667 16.6667 5.33333 16.6667 530 531 Line 7.33333 10 7.33333 10.6667 530 613 Line 6.66667 10 6.66667 10.6667 530 823 Line 6.66667 10.6667 7.33333 10.6667 531 824 Line 7.33333 10.6667 8 10.6667 532 752 Line 17.3333 7.33333 18 7.33333 532 896 Line 18 7.33333 18 8 533 534 Line 7.33333 6 7.33333 6.66667 533 985 Line 6.66667 6 6.66667 6.66667 535 616 Line 4 9.33333 4.66667 9.33333 535 747 Line 4.66667 8.66667 4.66667 9.33333 535 853 Line 4 8.66667 4.66667 8.66667 536 -1 Line 34 6.66667 34 7.33333 536 642 Line 33.3333 6.66667 33.3333 7.33333 537 629 Line 14 19.3333 14 20 538 539 Line 14.6667 2 14.6667 2.66667 538 571 Line 14 2 14 2.66667 538 766 Line 14 2.66667 14.6667 2.66667 538 771 Line 14 2 14.6667 2 539 772 Line 14.6667 2 15.3333 2 542 543 Line 23.3333 15.3333 24 15.3333 542 678 Line 23.3333 14.6667 24 14.6667 542 849 Line 23.3333 14.6667 23.3333 15.3333 542 865 Line 24 14.6667 24 15.3333 543 693 Line 23.3333 16 24 16 543 850 Line 23.3333 15.3333 23.3333 16 543 863 Line 24 15.3333 24 16 544 545 Line 10.6667 15.3333 10.6667 16 546 721 Line 9.33333 9.33333 10 9.33333 546 740 Line 10 9.33333 10 10 546 997 Line 9.33333 10 10 10 547 548 Line 3.33333 10 4 10 547 616 Line 4 9.33333 4 10 548 612 Line 4 10 4 10.6667 549 550 Line 3.33333 6 4 6 551 552 Line 4.66667 3.33333 4.66667 4 551 730 Line 4 3.33333 4.66667 3.33333 552 731 Line 4.66667 3.33333 5.33333 3.33333 554 555 Line 12 2.66667 12 3.33333 554 965 Line 11.3333 3.33333 12 3.33333 555 966 Line 12 3.33333 12.6667 3.33333 556 557 Line 14 10.6667 14.6667 10.6667 558 872 Line 22 19.3333 22.6667 19.3333 559 560 Line 12.6667 13.3333 13.3333 13.3333 559 562 Line 13.3333 12.6667 13.3333 13.3333 559 609 Line 12.6667 12.6667 13.3333 12.6667 560 591 Line 13.3333 13.3333 13.3333 14 560 811 Line 12.6667 13.3333 12.6667 14 561 569 Line 14.6667 13.3333 15.3333 13.3333 561 592 Line 14.6667 13.3333 14.6667 14 561 750 Line 14.6667 14 15.3333 14 562 568 Line 14 12.6667 14 13.3333 562 591 Line 13.3333 13.3333 14 13.3333 563 587 Line 7.33333 4.66667 7.33333 5.33333 563 783 Line 6.66667 4.66667 6.66667 5.33333 563 831 Line 6.66667 4.66667 7.33333 4.66667 565 -1 Line 26 22.6667 26.6667 22.6667 565 835 Line 26 22 26 22.6667 566 567 Line 10.6667 3.33333 10.6667 4 566 625 Line 10 3.33333 10 4 567 965 Line 11.3333 3.33333 11.3333 4 568 569 Line 14.6667 12.6667 14.6667 13.3333 568 592 Line 14 13.3333 14.6667 13.3333 569 739 Line 14.6667 12.6667 15.3333 12.6667 570 571 Line 13.3333 2 13.3333 2.66667 572 573 Line 20.6667 12.6667 21.3333 12.6667 573 627 Line 20.6667 13.3333 21.3333 13.3333 573 669 Line 20.6667 12.6667 20.6667 13.3333 573 773 Line 21.3333 12.6667 21.3333 13.3333 574 575 Line 4.66667 10.6667 4.66667 11.3333 574 612 Line 4 10.6667 4.66667 10.6667 575 757 Line 4.66667 10.6667 5.33333 10.6667 576 618 Line 26 2.66667 26 3.33333 576 821 Line 26 3.33333 26.6667 3.33333 576 962 Line 26 2.66667 26.6667 2.66667 577 578 Line 17.3333 13.3333 18 13.3333 579 580 Line 23.3333 13.3333 24 13.3333 579 939 Line 24 12.6667 24 13.3333 579 986 Line 23.3333 12.6667 24 12.6667 580 678 Line 23.3333 14 24 14 580 940 Line 24 13.3333 24 14 581 582 Line 12.6667 4 13.3333 4 581 753 Line 13.3333 3.33333 13.3333 4 581 966 Line 12.6667 3.33333 12.6667 4 582 754 Line 13.3333 4 13.3333 4.66667 583 584 Line 30.6667 5.33333 30.6667 6 583 884 Line 30 5.33333 30.6667 5.33333 584 604 Line 30.6667 5.33333 31.3333 5.33333 585 586 Line 6 17.3333 6 18 586 894 Line 6.66667 17.3333 6.66667 18 587 588 Line 8 4.66667 8 5.33333 589 590 Line 7.33333 8.66667 7.33333 9.33333 589 601 Line 6.66667 8.66667 7.33333 8.66667 590 602 Line 7.33333 8.66667 8 8.66667 590 860 Line 8 8.66667 8 9.33333 591 592 Line 14 13.3333 14 14 592 749 Line 14 14 14.6667 14 593 594 Line 26 8.66667 26.6667 8.66667 593 691 Line 26.6667 8 26.6667 8.66667 594 692 Line 26.6667 8.66667 26.6667 9.33333 595 707 Line 10 6 10.6667 6 595 712 Line 10.6667 5.33333 10.6667 6 595 976 Line 10 5.33333 10.6667 5.33333 596 597 Line 2 8 2.66667 8 596 807 Line 2 7.33333 2.66667 7.33333 597 698 Line 2.66667 8 2.66667 8.66667 598 -1 Line 0 2 0 2.66667 598 599 Line 0.666667 2 0.666667 2.66667 599 977 Line 0.666667 2.66667 1.33333 2.66667 600 980 Line 1.33333 4.66667 1.33333 5.33333 601 602 Line 7.33333 8 7.33333 8.66667 601 665 Line 6.66667 8 7.33333 8 602 666 Line 7.33333 8 8 8 602 859 Line 8 8 8 8.66667 603 604 Line 30.6667 4.66667 31.3333 4.66667 603 854 Line 31.3333 4 31.3333 4.66667 603 883 Line 30.6667 4 30.6667 4.66667 604 884 Line 30.6667 4.66667 30.6667 5.33333 605 606 Line 12 14 12 14.6667 606 811 Line 12 14 12.6667 14 607 874 Line 15.3333 6.66667 16 6.66667 608 609 Line 12.6667 12 12.6667 12.6667 610 611 Line 16 2.66667 16 3.33333 611 662 Line 16 3.33333 16.6667 3.33333 612 616 Line 4 10 4.66667 10 612 757 Line 4.66667 10 4.66667 10.6667 613 617 Line 6 10 6.66667 10 613 758 Line 6 10 6 10.6667 615 644 Line 28 9.33333 28.6667 9.33333 615 845 Line 28 8.66667 28.6667 8.66667 615 917 Line 28.6667 8.66667 28.6667 9.33333 616 654 Line 4.66667 9.33333 4.66667 10 617 655 Line 6 9.33333 6 10 618 619 Line 25.3333 2.66667 26 2.66667 618 760 Line 25.3333 2.66667 25.3333 3.33333 618 819 Line 25.3333 3.33333 26 3.33333 619 704 Line 25.3333 2 26 2 619 962 Line 26 2 26 2.66667 620 621 Line 13.3333 8.66667 14 8.66667 620 781 Line 13.3333 8 13.3333 8.66667 620 949 Line 14 8 14 8.66667 621 718 Line 14 8.66667 14 9.33333 621 762 Line 13.3333 9.33333 14 9.33333 622 623 Line 30 7.33333 30 8 624 -1 Line 2.66667 0 3.33333 0 625 626 Line 9.33333 4 10 4 625 645 Line 9.33333 3.33333 9.33333 4 626 646 Line 9.33333 4 9.33333 4.66667 627 628 Line 20.6667 14 21.3333 14 627 670 Line 20.6667 13.3333 20.6667 14 629 630 Line 14.6667 19.3333 14.6667 20 631 -1 Line 28.6667 0 29.3333 0 632 633 Line 30.6667 11.3333 30.6667 12 632 826 Line 30 11.3333 30 12 632 938 Line 30 11.3333 30.6667 11.3333 633 921 Line 30.6667 11.3333 31.3333 11.3333 635 807 Line 2.66667 6.66667 2.66667 7.33333 636 977 Line 1.33333 2.66667 1.33333 3.33333 637 -1 Line 18.6667 22.6667 19.3333 22.6667 637 951 Line 18.6667 22 19.3333 22 638 986 Line 23.3333 12 23.3333 12.6667 639 640 Line 16.6667 10 17.3333 10 639 998 Line 17.3333 9.33333 17.3333 10 640 703 Line 16.6667 10.6667 17.3333 10.6667 641 697 Line 20 16 20 16.6667 641 919 Line 19.3333 16 20 16 642 971 Line 32.6667 7.33333 33.3333 7.33333 643 -1 Line 24.6667 0 25.3333 0 643 705 Line 25.3333 0 25.3333 0.666667 643 943 Line 24.6667 0.666667 25.3333 0.666667 644 904 Line 28 10 28.6667 10 645 646 Line 8.66667 4 9.33333 4 645 788 Line 8.66667 3.33333 9.33333 3.33333 645 817 Line 8.66667 3.33333 8.66667 4 647 648 Line 8 16.6667 8 17.3333 647 658 Line 7.33333 16.6667 7.33333 17.3333 647 895 Line 7.33333 17.3333 8 17.3333 648 893 Line 8 17.3333 8.66667 17.3333 649 650 Line 24.6667 12 25.3333 12 649 879 Line 24.6667 11.3333 24.6667 12 649 933 Line 25.3333 11.3333 25.3333 12 650 759 Line 24.6667 12.6667 25.3333 12.6667 650 889 Line 25.3333 12 25.3333 12.6667 650 987 Line 24.6667 12 24.6667 12.6667 651 671 Line 8.66667 7.33333 8.66667 8 651 743 Line 8.66667 8 9.33333 8 651 926 Line 9.33333 7.33333 9.33333 8 652 653 Line 32 9.33333 32 10 653 769 Line 32.6667 9.33333 32.6667 10 653 888 Line 32 9.33333 32.6667 9.33333 654 655 Line 5.33333 9.33333 5.33333 10 654 747 Line 4.66667 9.33333 5.33333 9.33333 654 757 Line 4.66667 10 5.33333 10 655 748 Line 5.33333 9.33333 6 9.33333 655 758 Line 5.33333 10 6 10 656 657 Line 22.6667 17.3333 23.3333 17.3333 656 694 Line 23.3333 16.6667 23.3333 17.3333 658 792 Line 6.66667 16.6667 7.33333 16.6667 658 894 Line 6.66667 17.3333 7.33333 17.3333 659 660 Line 32.6667 11.3333 33.3333 11.3333 659 770 Line 32.6667 10.6667 33.3333 10.6667 660 908 Line 33.3333 11.3333 33.3333 12 662 663 Line 16 4 16.6667 4 662 722 Line 16.6667 3.33333 16.6667 4 663 676 Line 16.6667 4 16.6667 4.66667 664 960 Line 8.66667 10.6667 9.33333 10.6667 664 997 Line 9.33333 10 9.33333 10.6667 665 666 Line 7.33333 7.33333 7.33333 8 666 671 Line 8 7.33333 8 8 667 668 Line 10 8.66667 10.6667 8.66667 667 672 Line 10.6667 8 10.6667 8.66667 667 720 Line 10 8 10 8.66667 667 927 Line 10 8 10.6667 8 668 721 Line 10 8.66667 10 9.33333 668 725 Line 10.6667 8.66667 10.6667 9.33333 668 740 Line 10 9.33333 10.6667 9.33333 669 670 Line 20 13.3333 20.6667 13.3333 671 859 Line 8 8 8.66667 8 672 673 Line 11.3333 8 11.3333 8.66667 672 725 Line 10.6667 8.66667 11.3333 8.66667 672 928 Line 10.6667 8 11.3333 8 673 726 Line 11.3333 8.66667 12 8.66667 673 755 Line 11.3333 8 12 8 673 780 Line 12 8 12 8.66667 674 675 Line 19.3333 4.66667 19.3333 5.33333 675 838 Line 20 4.66667 20 5.33333 675 969 Line 19.3333 5.33333 20 5.33333 676 677 Line 16.6667 4.66667 17.3333 4.66667 676 722 Line 16.6667 4 17.3333 4 676 808 Line 17.3333 4 17.3333 4.66667 677 877 Line 16.6667 5.33333 17.3333 5.33333 678 679 Line 24 14 24 14.6667 679 764 Line 24.6667 14 24.6667 14.6667 679 865 Line 24 14.6667 24.6667 14.6667 679 940 Line 24 14 24.6667 14 680 681 Line 6.66667 2.66667 6.66667 3.33333 681 830 Line 6.66667 3.33333 7.33333 3.33333 682 683 Line 23.3333 8.66667 24 8.66667 684 685 Line 5.33333 2 5.33333 2.66667 684 687 Line 4.66667 2 4.66667 2.66667 684 731 Line 4.66667 2.66667 5.33333 2.66667 686 687 Line 4 2 4 2.66667 687 730 Line 4 2.66667 4.66667 2.66667 688 689 Line 12 1.33333 12 2 688 742 Line 11.3333 1.33333 11.3333 2 691 692 Line 26.6667 8.66667 27.3333 8.66667 693 694 Line 23.3333 16.6667 24 16.6667 694 922 Line 24 16.6667 24 17.3333 695 696 Line 21.3333 15.3333 22 15.3333 697 945 Line 20 16 20.6667 16 698 699 Line 2.66667 8.66667 3.33333 8.66667 698 735 Line 3.33333 8 3.33333 8.66667 700 701 Line 4 12.6667 4 13.3333 700 806 Line 3.33333 12.6667 4 12.6667 700 953 Line 3.33333 12.6667 3.33333 13.3333 702 703 Line 16.6667 10.6667 16.6667 11.3333 704 706 Line 25.3333 1.33333 26 1.33333 704 961 Line 26 1.33333 26 2 705 -1 Line 25.3333 0 26 0 705 706 Line 25.3333 0.666667 26 0.666667 706 943 Line 25.3333 0.666667 25.3333 1.33333 707 708 Line 10.6667 6 10.6667 6.66667 708 712 Line 10.6667 6 11.3333 6 708 887 Line 10.6667 6.66667 11.3333 6.66667 709 710 Line 16 1.33333 16 2 709 772 Line 15.3333 1.33333 15.3333 2 710 957 Line 16.6667 1.33333 16.6667 2 712 713 Line 11.3333 5.33333 11.3333 6 712 988 Line 10.6667 5.33333 11.3333 5.33333 713 989 Line 11.3333 5.33333 12 5.33333 714 754 Line 13.3333 4.66667 14 4.66667 714 786 Line 14 4.66667 14 5.33333 715 -1 Line 11.3333 0 12 0 715 716 Line 12 0 12 0.666667 716 -1 Line 12 0 12.6667 0 717 959 Line 29.3333 12.6667 30 12.6667 718 949 Line 14 8.66667 14.6667 8.66667 719 -1 Line 0 6.66667 0 7.33333 719 775 Line 0 7.33333 0.666667 7.33333 720 721 Line 9.33333 8.66667 10 8.66667 720 743 Line 9.33333 8 9.33333 8.66667 720 926 Line 9.33333 8 10 8 721 744 Line 9.33333 8.66667 9.33333 9.33333 723 -1 Line 16.6667 0 17.3333 0 723 724 Line 16.6667 0.666667 17.3333 0.666667 724 957 Line 16.6667 1.33333 17.3333 1.33333 725 726 Line 11.3333 8.66667 11.3333 9.33333 727 728 Line 6.66667 12.6667 6.66667 13.3333 727 784 Line 6 13.3333 6.66667 13.3333 728 729 Line 7.33333 12.6667 7.33333 13.3333 730 731 Line 4.66667 2.66667 4.66667 3.33333 732 733 Line 25.3333 6.66667 25.3333 7.33333 734 735 Line 3.33333 8 4 8 734 852 Line 4 7.33333 4 8 735 853 Line 4 8 4 8.66667 736 772 Line 14.6667 1.33333 15.3333 1.33333 738 739 Line 14.6667 12 15.3333 12 740 741 Line 10 10 10.6667 10 741 841 Line 10.6667 10 10.6667 10.6667 741 997 Line 10 10 10 10.6667 743 744 Line 8.66667 8.66667 9.33333 8.66667 743 859 Line 8.66667 8 8.66667 8.66667 744 860 Line 8.66667 8.66667 8.66667 9.33333 745 746 Line 16.6667 6.66667 17.3333 6.66667 745 751 Line 17.3333 6 17.3333 6.66667 745 877 Line 16.6667 6 17.3333 6 746 752 Line 17.3333 6.66667 17.3333 7.33333 747 748 Line 5.33333 8.66667 5.33333 9.33333 749 750 Line 14.6667 14 14.6667 14.6667 750 942 Line 14.6667 14.6667 15.3333 14.6667 751 752 Line 17.3333 6.66667 18 6.66667 751 897 Line 18 6 18 6.66667 752 898 Line 18 6.66667 18 7.33333 753 754 Line 13.3333 4 14 4 753 767 Line 14 3.33333 14 4 754 785 Line 14 4 14 4.66667 755 756 Line 11.3333 7.33333 12 7.33333 755 928 Line 11.3333 7.33333 11.3333 8 756 887 Line 11.3333 6.66667 11.3333 7.33333 757 758 Line 5.33333 10 5.33333 10.6667 759 848 Line 24.6667 13.3333 25.3333 13.3333 759 939 Line 24.6667 12.6667 24.6667 13.3333 760 799 Line 24.6667 3.33333 25.3333 3.33333 762 946 Line 13.3333 9.33333 13.3333 10 763 765 Line 26 14 26 14.6667 764 765 Line 25.3333 14 25.3333 14.6667 764 848 Line 24.6667 14 25.3333 14 764 866 Line 24.6667 14.6667 25.3333 14.6667 766 767 Line 14 3.33333 14.6667 3.33333 767 785 Line 14 4 14.6667 4 769 770 Line 32.6667 10 33.3333 10 771 772 Line 14.6667 1.33333 14.6667 2 775 -1 Line 0 7.33333 0 8 776 777 Line 1.33333 12.6667 1.33333 13.3333 778 779 Line 20 10 20.6667 10 778 973 Line 20 9.33333 20.6667 9.33333 778 982 Line 20 9.33333 20 10 779 983 Line 20 10 20 10.6667 780 781 Line 12.6667 8 12.6667 8.66667 782 783 Line 6 4.66667 6 5.33333 782 801 Line 5.33333 4.66667 6 4.66667 783 802 Line 6 4.66667 6.66667 4.66667 785 786 Line 14 4.66667 14.6667 4.66667 787 788 Line 8.66667 2.66667 8.66667 3.33333 787 817 Line 8 3.33333 8.66667 3.33333 789 934 Line 22 6.66667 22.6667 6.66667 789 958 Line 22 6 22.6667 6 790 -1 Line 34 4 34 4.66667 790 791 Line 33.3333 4.66667 34 4.66667 790 839 Line 33.3333 4 33.3333 4.66667 791 -1 Line 34 4.66667 34 5.33333 791 840 Line 33.3333 4.66667 33.3333 5.33333 793 794 Line 18.6667 10.6667 19.3333 10.6667 793 983 Line 19.3333 10 19.3333 10.6667 795 796 Line 20.6667 22 21.3333 22 795 996 Line 21.3333 21.3333 21.3333 22 796 -1 Line 20.6667 22.6667 21.3333 22.6667 798 800 Line 24.6667 4.66667 25.3333 4.66667 798 803 Line 25.3333 4.66667 25.3333 5.33333 798 857 Line 24.6667 4.66667 24.6667 5.33333 799 800 Line 24.6667 4 25.3333 4 799 819 Line 25.3333 3.33333 25.3333 4 800 820 Line 25.3333 4 25.3333 4.66667 800 858 Line 24.6667 4 24.6667 4.66667 801 802 Line 6 4 6 4.66667 802 831 Line 6.66667 4 6.66667 4.66667 803 804 Line 26 4.66667 26 5.33333 803 820 Line 25.3333 4.66667 26 4.66667 804 822 Line 26 4.66667 26.6667 4.66667 805 806 Line 3.33333 12 4 12 806 952 Line 3.33333 12 3.33333 12.6667 807 903 Line 2 6.66667 2.66667 6.66667 808 809 Line 18 4 18 4.66667 814 815 Line 27.3333 0.666667 27.3333 1.33333 816 817 Line 8 3.33333 8 4 816 830 Line 7.33333 3.33333 7.33333 4 819 820 Line 25.3333 4 26 4 819 821 Line 26 3.33333 26 4 820 822 Line 26 4 26 4.66667 821 822 Line 26 4 26.6667 4 823 824 Line 7.33333 10.6667 7.33333 11.3333 825 826 Line 29.3333 11.3333 29.3333 12 826 959 Line 29.3333 12 30 12 827 -1 Line 32 22.6667 32.6667 22.6667 827 828 Line 32.6667 22 32.6667 22.6667 827 829 Line 32 22 32 22.6667 828 -1 Line 32.6667 22.6667 33.3333 22.6667 828 836 Line 33.3333 22 33.3333 22.6667 829 -1 Line 31.3333 22.6667 32 22.6667 830 831 Line 6.66667 4 7.33333 4 832 833 Line 25.3333 21.3333 25.3333 22 832 834 Line 24.6667 22 25.3333 22 832 995 Line 24.6667 21.3333 24.6667 22 833 835 Line 25.3333 22 26 22 834 -1 Line 24.6667 22.6667 25.3333 22.6667 834 835 Line 25.3333 22 25.3333 22.6667 835 -1 Line 25.3333 22.6667 26 22.6667 836 -1 Line 33.3333 22.6667 34 22.6667 836 882 Line 33.3333 22 34 22 837 838 Line 20 4.66667 20.6667 4.66667 838 970 Line 20 5.33333 20.6667 5.33333 839 840 Line 32.6667 4.66667 33.3333 4.66667 839 855 Line 32.6667 4 32.6667 4.66667 841 842 Line 11.3333 10 11.3333 10.6667 842 936 Line 12 10 12 10.6667 843 885 Line 29.3333 4 30 4 844 845 Line 28 8 28.6667 8 846 847 Line 16 8.66667 16.6667 8.66667 846 905 Line 16 8 16 8.66667 847 906 Line 16 8.66667 16 9.33333 848 940 Line 24.6667 13.3333 24.6667 14 849 850 Line 22.6667 15.3333 23.3333 15.3333 851 852 Line 4 7.33333 4.66667 7.33333 852 853 Line 4 8 4.66667 8 854 855 Line 32 4 32 4.66667 856 857 Line 24 4.66667 24 5.33333 856 984 Line 23.3333 4.66667 23.3333 5.33333 857 858 Line 24 4.66667 24.6667 4.66667 859 860 Line 8 8.66667 8.66667 8.66667 861 862 Line 15.3333 10 16 10 861 906 Line 15.3333 9.33333 16 9.33333 863 864 Line 24.6667 15.3333 24.6667 16 863 865 Line 24 15.3333 24.6667 15.3333 864 866 Line 24.6667 15.3333 25.3333 15.3333 865 866 Line 24.6667 14.6667 24.6667 15.3333 867 868 Line 4 16.6667 4 17.3333 868 990 Line 4.66667 16.6667 4.66667 17.3333 870 892 Line 32.6667 15.3333 32.6667 16 871 880 Line 21.3333 5.33333 21.3333 6 871 970 Line 20.6667 5.33333 20.6667 6 872 873 Line 22 20 22.6667 20 874 875 Line 15.3333 7.33333 16 7.33333 875 905 Line 15.3333 8 16 8 876 877 Line 16.6667 5.33333 16.6667 6 878 879 Line 24 11.3333 24.6667 11.3333 879 987 Line 24 12 24.6667 12 880 958 Line 22 5.33333 22 6 881 -1 Line 34 20.6667 34 21.3333 881 882 Line 33.3333 21.3333 34 21.3333 882 -1 Line 34 21.3333 34 22 883 884 Line 30 4.66667 30.6667 4.66667 883 885 Line 30 4 30 4.66667 884 886 Line 30 4.66667 30 5.33333 885 886 Line 29.3333 4.66667 30 4.66667 887 928 Line 10.6667 7.33333 11.3333 7.33333 889 890 Line 26 12 26 12.6667 889 933 Line 25.3333 12 26 12 890 931 Line 26 12 26.6667 12 891 892 Line 32.6667 15.3333 33.3333 15.3333 891 911 Line 33.3333 14.6667 33.3333 15.3333 892 912 Line 33.3333 15.3333 33.3333 16 893 895 Line 8 17.3333 8 18 894 895 Line 7.33333 17.3333 7.33333 18 896 898 Line 18 7.33333 18.6667 7.33333 897 898 Line 18 6.66667 18.6667 6.66667 899 900 Line 8.66667 14 9.33333 14 901 918 Line 19.3333 14.6667 19.3333 15.3333 902 903 Line 2 6 2.66667 6 904 963 Line 28 10 28 10.6667 905 906 Line 15.3333 8.66667 16 8.66667 907 -1 Line 0 20 0 20.6667 908 -1 Line 34 11.3333 34 12 908 923 Line 33.3333 12 34 12 909 910 Line 15.3333 15.3333 16 15.3333 909 942 Line 15.3333 14.6667 15.3333 15.3333 911 -1 Line 34 14.6667 34 15.3333 911 912 Line 33.3333 15.3333 34 15.3333 912 -1 Line 34 15.3333 34 16 913 914 Line 16.6667 12 16.6667 12.6667 915 916 Line 28.6667 2.66667 29.3333 2.66667 918 919 Line 19.3333 15.3333 20 15.3333 918 944 Line 20 14.6667 20 15.3333 919 945 Line 20 15.3333 20 16 920 921 Line 30.6667 10.6667 31.3333 10.6667 920 937 Line 30.6667 10 30.6667 10.6667 921 938 Line 30.6667 10.6667 30.6667 11.3333 923 -1 Line 34 12 34 12.6667 923 924 Line 33.3333 12.6667 34 12.6667 924 -1 Line 34 12.6667 34 13.3333 926 927 Line 10 7.33333 10 8 927 928 Line 10.6667 7.33333 10.6667 8 930 931 Line 26 11.3333 26.6667 11.3333 930 932 Line 26 10.6667 26 11.3333 931 933 Line 26 11.3333 26 12 932 933 Line 25.3333 11.3333 26 11.3333 935 936 Line 12 10 12.6667 10 935 946 Line 12.6667 9.33333 12.6667 10 936 947 Line 12.6667 10 12.6667 10.6667 937 938 Line 30 10.6667 30.6667 10.6667 939 940 Line 24 13.3333 24.6667 13.3333 939 987 Line 24 12.6667 24.6667 12.6667 944 945 Line 20 15.3333 20.6667 15.3333 946 947 Line 12.6667 10 13.3333 10 950 951 Line 18.6667 21.3333 19.3333 21.3333 952 953 Line 2.66667 12.6667 3.33333 12.6667 955 956 Line 10 14 10.6667 14 961 962 Line 26 2 26.6667 2 963 964 Line 27.3333 10.6667 28 10.6667 965 966 Line 12 3.33333 12 4 967 968 Line 27.3333 12.6667 27.3333 13.3333 969 970 Line 20 5.33333 20 6 971 972 Line 32.6667 8 33.3333 8 973 974 Line 20.6667 8.66667 20.6667 9.33333 976 988 Line 10.6667 4.66667 10.6667 5.33333 977 978 Line 0.666667 3.33333 1.33333 3.33333 978 979 Line 0.666667 4 1.33333 4 979 980 Line 0.666667 4.66667 1.33333 4.66667 982 983 Line 19.3333 10 20 10 986 987 Line 24 12 24 12.6667 988 989 Line 11.3333 4.66667 11.3333 5.33333 991 992 Line 0.666667 10 1.33333 10 993 -1 Line 4 0 4.66667 0 993 994 Line 4 0.666667 4.66667 0.666667 999 1000 Line 11.3333 11.3333 11.3333 12 ================================================ FILE: src/libs/mazes/include/mazes/breadthfirstsearch.h ================================================ #ifndef BREADTHFIRSTSEARCH_H #define BREADTHFIRSTSEARCH_H #include "spanningtreealgorithm.h" #include class BreadthFirstSearch : public SpanningtreeAlgorithm { public: std::vector> SpanningTree(int, const Graph&); private: std::vector visited; std::vector currentlevel, nextlevel; }; #endif /* end of include guard: BREADTHFIRSTSEARCH_H */ ================================================ FILE: src/libs/mazes/include/mazes/cellborder.h ================================================ #ifndef CELLBORDER_H #define CELLBORDER_H #include #include class CellBorder { public: virtual ~CellBorder() = default; virtual std::string GnuplotPrintString() const = 0; virtual std::string SVGPrintString() const = 0; }; class LineBorder : public CellBorder { public: std::string GnuplotPrintString() const override; std::string SVGPrintString() const override; LineBorder(double, double, double, double); explicit LineBorder(std::tuple); [[nodiscard]] std::tuple getBorderCoords() const { return std::make_tuple(x1_, y1_, x2_, y2_); } protected: double x1_, y1_, x2_, y2_; }; class ArcBorder : public CellBorder { public: std::string GnuplotPrintString() const override; std::string SVGPrintString() const override; ArcBorder(double, double, double, double, double); protected: double cx_, cy_, r_, theta1_, theta2_; }; #endif /* end of include guard: CELLBORDER_H */ ================================================ FILE: src/libs/mazes/include/mazes/circularhexagonmaze.h ================================================ #ifndef CIRCULARHEXAGONMAZE_H #define CIRCULARHEXAGONMAZE_H #include "hexagonalmaze.h" class CircularHexagonMaze : public HexagonalMaze { public: explicit CircularHexagonMaze(int); protected: std::shared_ptr GetEdge(int, int, int, int) const override; std::tuple GetCoordinateBounds() const override; }; #endif /* end of include guard: CIRCULARHEXAGONMAZE_H */ ================================================ FILE: src/libs/mazes/include/mazes/circularmaze.h ================================================ #ifndef CIRCULARMAZE_H #define CIRCULARMAZE_H #include "maze.h" class CircularMaze : public Maze { public: CircularMaze(int); virtual void InitialiseGraph(); protected: int size_; std::vector ringnodecount_, ringnodeprefixsum_; std::tuple GetCoordinateBounds() const; }; #endif /* end of include guard: CIRCULARMAZE_H */ ================================================ FILE: src/libs/mazes/include/mazes/depthfirstsearch.h ================================================ #ifndef DEPTHFIRSTSEARCH_H #define DEPTHFIRSTSEARCH_H #include "spanningtreealgorithm.h" #include class DepthFirstSearch : public SpanningtreeAlgorithm { public: std::vector> SpanningTree(int, const Graph &); private: std::vector visited; void DFS(int, const Graph &); }; #endif /* end of include guard: DEPTHFIRSTSEARCH_H */ ================================================ FILE: src/libs/mazes/include/mazes/hexagonalmaze.h ================================================ #ifndef HEXAGONALMAZE_H #define HEXAGONALMAZE_H #include "maze.h" class HexagonalMaze : public Maze { public: explicit HexagonalMaze(int); void InitialiseGraph() override; protected: int size_; virtual std::shared_ptr GetEdge(int, int, int, int) const; int VertexIndex(int, int, int, int) const; std::tuple GetCoordinateBounds() const override; }; #endif /* end of include guard: HEXAGONALMAZE_H */ ================================================ FILE: src/libs/mazes/include/mazes/honeycombmaze.h ================================================ #ifndef HONEYCOMBMAZE_H #define HONEYCOMBMAZE_H #include "maze.h" class HoneyCombMaze : public Maze { public: explicit HoneyCombMaze(int); void InitialiseGraph() override; std::tuple GetCoordinateBounds() const override; public: bool bordersForEntranceAndExit = true; protected: int size_; static const int neigh[6][2]; [[nodiscard]] int VertexIndex(int, int) const; [[nodiscard]] virtual std::tuple GetEdge(int, int, int) const; [[nodiscard]] static std::pair GetCenter(int u, int v) ; std::pair VExtent(int); bool IsValidNode(int, int); }; #endif /* end of include guard: HONEYCOMBMAZE_H */ ================================================ FILE: src/libs/mazes/include/mazes/kruskal.h ================================================ #ifndef KRUSKAL_H #define KRUSKAL_H #include "spanningtreealgorithm.h" #include class Kruskal : public SpanningtreeAlgorithm { public: std::vector> SpanningTree(int, const Graph&); private: std::vector parent_; int GetParent(int); }; #endif /* end of include guard: KRUSKAL_H */ ================================================ FILE: src/libs/mazes/include/mazes/looperasedrandomwalk.h ================================================ #ifndef LOOPERASEDRANDOMWALK_H #define LOOPERASEDRANDOMWALK_H #include "spanningtreealgorithm.h" #include class LoopErasedRandomWalk : public SpanningtreeAlgorithm { public: std::vector> SpanningTree(int, const Graph &); private: std::vector visited; void LERW(int, int, const Graph &); }; #endif /* end of include guard: LOOPERASEDRANDOMWALK_H */ ================================================ FILE: src/libs/mazes/include/mazes/maze.h ================================================ #ifndef MAZE_H #define MAZE_H #include "cellborder.h" #include "spanningtreealgorithm.h" #include #include using AdjList = std::vector>>>; class Maze { public: explicit Maze(int = 0, int = 0, int = 1); virtual ~Maze() = default; void GenerateMaze(SpanningtreeAlgorithm*); void PrintMazeGnuplot(const std::string&) const; void PrintMazeSVG(const std::string&) const; void RemoveBorders(const std::vector>&); virtual void InitialiseGraph() = 0; AdjList & getAdjacencyList() { return adjacencylist_; } std::vector> & getCellCenters() { return cellCenters; } virtual std::tuple GetCoordinateBounds() const = 0; protected: // Solving a maze is equivalent to finding a path in a graph int vertices_; AdjList adjacencylist_; int startvertex_, endvertex_; std::vector> cellCenters; }; #endif /* end of include guard: MAZE_H */ ================================================ FILE: src/libs/mazes/include/mazes/prim.h ================================================ #ifndef PRIM_H #define PRIM_H #include "spanningtreealgorithm.h" #include class Prim : public SpanningtreeAlgorithm { public: std::vector> SpanningTree(int, const Graph &); private: void PrimAlgorithm(int, const Graph &); }; #endif /* end of include guard: PRIM_H */ ================================================ FILE: src/libs/mazes/include/mazes/rectangularmaze.h ================================================ #include "maze.h" class RectangularMaze : public Maze { public: RectangularMaze(int, int); void InitialiseGraph() override; private: int width_, height_; int VertexIndex(int, int); std::tuple GetCoordinateBounds() const override; }; ================================================ FILE: src/libs/mazes/include/mazes/spanningtreealgorithm.h ================================================ #ifndef SPANNINGTREEALGORITHM_H #define SPANNINGTREEALGORITHM_H #include "cellborder.h" #include #include #include typedef std::vector>>> Graph; class SpanningtreeAlgorithm { public: SpanningtreeAlgorithm(); virtual std::vector> SpanningTree(int, const Graph&) = 0; protected: std::vector> spanningtree; std::random_device randomdevice; std::mt19937 generator; }; #endif /* end of include guard: SPANNINGTREEALGORITHM_H */ ================================================ FILE: src/libs/mazes/include/mazes/usermaze.h ================================================ #include "maze.h" class UserMaze : public Maze { public: UserMaze(std::string); virtual void InitialiseGraph(); private: double xmin_, ymin_, xmax_, ymax_; std::string filename_; virtual std::tuple GetCoordinateBounds() const; }; ================================================ FILE: src/libs/mazes/src/breadthfirstsearch.cpp ================================================ #include #include #include std::vector> BreadthFirstSearch::SpanningTree( int vertices, const Graph& adjacencylist) { visited = std::vector(vertices, false); int startvertex = std::uniform_int_distribution(0, vertices - 1)(generator); currentlevel.push_back(startvertex); visited[startvertex] = true; spanningtree.clear(); while (!currentlevel.empty()) { for (auto vertex : currentlevel) { for (const auto& edge : adjacencylist[vertex]) { int nextvertex = edge.first; if (nextvertex < 0 or visited[nextvertex]) continue; visited[nextvertex] = true; spanningtree.push_back({vertex, nextvertex}); nextlevel.push_back(nextvertex); } } currentlevel.clear(); swap(currentlevel, nextlevel); shuffle(currentlevel.begin(), currentlevel.end(), generator); } return spanningtree; } ================================================ FILE: src/libs/mazes/src/cellborder.cpp ================================================ #include #include #include LineBorder::LineBorder(double x1, double y1, double x2, double y2) : x1_(x1), y1_(y1), x2_(x2), y2_(y2) {} LineBorder::LineBorder(std::tuple xy) { std::tie(x1_, y1_, x2_, y2_) = xy; } std::string LineBorder::GnuplotPrintString() const { return "set arrow from " + std::to_string(x1_) + "," + std::to_string(y1_) + " to " + std::to_string(x2_) + "," + std::to_string(y2_) + " nohead lt -1 lw 2"; } std::string LineBorder::SVGPrintString() const { return ""; } ArcBorder::ArcBorder(double cx, double cy, double r, double theta1, double theta2) : cx_(cx), cy_(cy), r_(r), theta1_(theta1), theta2_(theta2) {} std::string ArcBorder::GnuplotPrintString() const { return "set parametric; plot [" + std::to_string(theta1_) + ":" + std::to_string(theta2_) + "] " + std::to_string(cx_) + "+cos(t)*" + std::to_string(r_) + "," + std::to_string(cy_) + "+sin(t)*" + std::to_string(r_) + " w l lw 2 lt -1 notitle;unset parametric"; } std::string ArcBorder::SVGPrintString() const { double x1 = cx_ + r_ * cos(theta1_), y1 = cy_ + r_ * sin(theta1_); double x2 = cx_ + r_ * cos(theta2_), y2 = cy_ + r_ * sin(theta2_); return ""; } ================================================ FILE: src/libs/mazes/src/circularhexagonmaze.cpp ================================================ #include #include #include CircularHexagonMaze::CircularHexagonMaze(int size) : HexagonalMaze(size) {} std::shared_ptr CircularHexagonMaze::GetEdge(int sector, int row, int column, int edge) const { if (edge == 0) { // Edge 0 is the bottom edge, hence connecting // (row+1,column)-(row+1,column+1) with an arc return std::make_shared( 0, 0, row + 1, (sector - 2) * M_PI / 3 + column * M_PI / 3 / (row + 1), (sector - 2) * M_PI / 3 + (column + 1) * M_PI / 3 / (row + 1)); } double ex1, ey1, ex2, ey2; if (edge == 1) { // (row,column)-(row+1,colum+1) double theta1 = (sector - 2) * M_PI / 3, theta2 = (sector - 2) * M_PI / 3; if (row > 0) theta1 += column * M_PI / 3 / row; theta2 += (column + 1) * M_PI / 3 / (row + 1); ex1 = row * cos(theta1); ey1 = row * sin(theta1); ex2 = (row + 1) * cos(theta2); ey2 = (row + 1) * sin(theta2); } else { // (row,column)-(row+1,colum) double theta1 = (sector - 2) * M_PI / 3, theta2 = (sector - 2) * M_PI / 3; if (row > 0) theta1 += column * M_PI / 3 / row; theta2 += column * M_PI / 3 / (row + 1); ex1 = row * cos(theta1); ey1 = row * sin(theta1); ex2 = (row + 1) * cos(theta2); ey2 = (row + 1) * sin(theta2); } return std::make_shared(ex1, ey1, ex2, ey2); } std::tuple CircularHexagonMaze::GetCoordinateBounds() const { return std::make_tuple(-size_, -size_, size_, size_); } ================================================ FILE: src/libs/mazes/src/circularmaze.cpp ================================================ #include #include #include CircularMaze::CircularMaze(int size) : size_(size) { ringnodecount_ = std::vector(size_); ringnodeprefixsum_ = std::vector(size_); ringnodecount_[0] = 1; ringnodeprefixsum_[0] = 0; for (int i = 1; i < size_; ++i) { ringnodecount_[i] = ringnodecount_[i - 1]; if (2 * M_PI * i / ringnodecount_[i - 1] > 2) ringnodecount_[i] *= 2; ringnodeprefixsum_[i] = ringnodeprefixsum_[i - 1] + ringnodecount_[i - 1]; } vertices_ = ringnodecount_.back() + ringnodeprefixsum_.back(); startvertex_ = ringnodeprefixsum_.back(); endvertex_ = startvertex_ + ringnodecount_.back() / 2; } void CircularMaze::InitialiseGraph() { Maze::InitialiseGraph(); for (int i = 1; i < size_; ++i) { for (int j = 0; j < ringnodecount_[i]; ++j) { int node = ringnodeprefixsum_[i] + j, nnode; std::shared_ptr ptr; nnode = ringnodeprefixsum_[i - 1] + (ringnodecount_[i - 1] * j) / ringnodecount_[i]; ptr = std::make_shared( 0, 0, i, j * 2 * M_PI / ringnodecount_[i] - M_PI / 2, (j + 1) * 2 * M_PI / ringnodecount_[i] - M_PI / 2); adjacencylist_[node].push_back({nnode, ptr}); adjacencylist_[nnode].push_back({node, ptr}); nnode = ringnodeprefixsum_[i] + ((j + 1) % ringnodecount_[i]); double theta = (j + 1) * 2 * M_PI / ringnodecount_[i] - M_PI / 2; ptr = std::make_shared(i * cos(theta), i * sin(theta), (i + 1) * cos(theta), (i + 1) * sin(theta)); adjacencylist_[node].push_back({nnode, ptr}); adjacencylist_[nnode].push_back({node, ptr}); if (i == size_ - 1 and node != startvertex_ and node != endvertex_) { ptr = std::make_shared( 0, 0, size_, j * 2 * M_PI / ringnodecount_[i] - M_PI / 2, (j + 1) * 2 * M_PI / ringnodecount_[i] - M_PI / 2); adjacencylist_[node].push_back({-1, ptr}); } } } } std::tuple CircularMaze::GetCoordinateBounds() const { return std::make_tuple(-size_, -size_, size_, size_); } ================================================ FILE: src/libs/mazes/src/depthfirstsearch.cpp ================================================ #include #include std::vector> DepthFirstSearch::SpanningTree( int vertices, const Graph& adjacencylist) { spanningtree.clear(); visited = std::vector(vertices, 0); DFS(std::uniform_int_distribution(0, vertices - 1)(generator), adjacencylist); return spanningtree; } void DepthFirstSearch::DFS(int vertex, const Graph& adjacencylist) { visited[vertex] = 1; std::vector nodeorder(adjacencylist[vertex].size()); std::iota(nodeorder.begin(), nodeorder.end(), 0); shuffle(nodeorder.begin(), nodeorder.end(), generator); for (auto index : nodeorder) { int nextvertex = adjacencylist[vertex][index].first; if (nextvertex < 0 or visited[nextvertex]) continue; spanningtree.push_back({vertex, nextvertex}); DFS(nextvertex, adjacencylist); } } ================================================ FILE: src/libs/mazes/src/hexagonalmaze.cpp ================================================ #include #include #include HexagonalMaze::HexagonalMaze(int size) : Maze(6 * size * size), size_(size) { startvertex_ = VertexIndex(0, 1, size_ - 1, 0); endvertex_ = VertexIndex(3, 1, size_ - 1, 0); } void HexagonalMaze::InitialiseGraph() { Maze::InitialiseGraph(); // Hexagon can be split into 6 triangular sectors // Each of which is subdivided into size_*size triangles for (int sector = 0; sector < 6; ++sector) { // Outer boundary, except entry and exit for (int i = 0; i < size_; ++i) { if ((i > 0) or (sector % 3 != 0)) { std::shared_ptr ptr = this->GetEdge(sector, size_ - 1, i, 0); adjacencylist_[VertexIndex(sector, 0, size_ - 1, i)].push_back( {-1, ptr}); } } // Border between the 6 major triangles for (int i = 0; i < size_; ++i) { std::shared_ptr ptr = this->GetEdge(sector, i, i, 1); adjacencylist_[VertexIndex(sector, 0, i, i)].push_back( {VertexIndex((sector + 1) % 6, 0, i, 0), ptr}); adjacencylist_[VertexIndex((sector + 1) % 6, 0, i, 0)].push_back( {VertexIndex(sector, 0, i, i), ptr}); } // 0-type edge // Between up vertex (i,j) and down vertex (i,j) for (int i = 0; i < size_ - 1; ++i) { for (int j = 0; j <= i; ++j) { std::shared_ptr ptr = this->GetEdge(sector, i, j, 0); adjacencylist_[VertexIndex(sector, 0, i, j)].push_back( {VertexIndex(sector, 1, i, j), ptr}); adjacencylist_[VertexIndex(sector, 1, i, j)].push_back( {VertexIndex(sector, 0, i, j), ptr}); } } // 1-type edge // Between up vertex (i,j) and down vertex (i-1,j) for (int i = 0; i < size_; ++i) { for (int j = 0; j < i; ++j) { std::shared_ptr ptr = this->GetEdge(sector, i, j, 1); adjacencylist_[VertexIndex(sector, 0, i, j)].push_back( {VertexIndex(sector, 1, i - 1, j), ptr}); adjacencylist_[VertexIndex(sector, 1, i - 1, j)].push_back( {VertexIndex(sector, 0, i, j), ptr}); } } // 2-type edge // Between up vertex (i,j) and down vertex (i-1,j-1) for (int i = 0; i < size_; ++i) { for (int j = 1; j <= i; ++j) { std::shared_ptr ptr = this->GetEdge(sector, i, j, 2); adjacencylist_[VertexIndex(sector, 0, i, j)].push_back( {VertexIndex(sector, 1, i - 1, j - 1), ptr}); adjacencylist_[VertexIndex(sector, 1, i - 1, j - 1)].push_back( {VertexIndex(sector, 0, i, j), ptr}); } } } } std::shared_ptr HexagonalMaze::GetEdge(int sector, int row, int column, int edge) const { // Coordinates of vertices of 0th sector double x1 = 0, y1 = 0, x2 = -size_ / 2.0, y2 = sqrt(3) * x2, x3 = -x2, y3 = y2; double dx12 = (x2 - x1) / size_, dy12 = (y2 - y1) / size_, dx23 = (x3 - x2) / size_, dy23 = (y3 - y2) / size_; double ex1, ey1, ex2, ey2; if (edge == 0) { // Edge 0 is the bottom edge, hence connecting // (row+1,column)-(row+1,column+1) ex1 = x1 + dx12 * (row + 1) + dx23 * column; ey1 = y1 + dy12 * (row + 1) + dy23 * column; ex2 = ex1 + dx23; ey2 = ey1 + dy23; } else if (edge == 1) { // (row,column)-(row+1,colum+1) ex1 = x1 + dx12 * row + dx23 * column; ey1 = y1 + dy12 * row + dy23 * column; ex2 = ex1 + dx12 + dx23; ey2 = ey1 + dy12 + dy23; } else { // (row,column)-(row+1,colum) ex1 = x1 + dx12 * row + dx23 * column; ey1 = y1 + dy12 * row + dy23 * column; ex2 = ex1 + dx12; ey2 = ey1 + dy12; } // Finally rotate to actual sector double theta = sector * M_PI / 3, sintheta = sin(theta), costheta = cos(theta); return std::make_shared( ex1 * costheta - ey1 * sintheta, ex1 * sintheta + ey1 * costheta, ex2 * costheta - ey2 * sintheta, ex2 * sintheta + ey2 * costheta); } int HexagonalMaze::VertexIndex(int sector, int updown, int row, int column) const { int vertexindex = sector * size_ * size_; if (updown == 1) vertexindex += (size_ * (size_ + 1)) / 2; vertexindex += (row * (row + 1)) / 2 + column; return vertexindex; } std::tuple HexagonalMaze::GetCoordinateBounds() const { return std::make_tuple(-size_, -sqrt(3) / 2 * size_, size_, sqrt(3) / 2 * size_); } ================================================ FILE: src/libs/mazes/src/honeycombmaze.cpp ================================================ #include #include const int HoneyCombMaze::neigh[6][2] = {{-1, 0}, {-1, 1}, {0, 1}, {1, 0}, {1, -1}, {0, -1}}; HoneyCombMaze::HoneyCombMaze(int size) : Maze(3 * size * (size - 1) + 1, 0, 3 * size * (size - 1)), size_(size) {} void HoneyCombMaze::InitialiseGraph() { Maze::InitialiseGraph(); for (int u = -size_ + 1; u < size_; ++u) { auto vextent = VExtent(u); for (int v = vextent.first; v <= vextent.second; ++v) { int node = VertexIndex(u, v); cellCenters[node] = GetCenter(u, v); for (int n = 0; n < 6; ++n) { int uu = u + neigh[n][0], vv = v + neigh[n][1]; if (IsValidNode(uu, vv)) { int nnode = VertexIndex(uu, vv); if (nnode > node) continue; std::shared_ptr ptr = std::make_shared(GetEdge(u, v, n)); adjacencylist_[node].push_back({nnode, ptr}); adjacencylist_[nnode].push_back({node, ptr}); } else { if (!bordersForEntranceAndExit) if ((node == startvertex_ and n == 0) or (node == endvertex_ and n == 3)) continue; adjacencylist_[node].push_back( {-1, std::make_shared(GetEdge(u, v, n))}); } } } } } // u, v are diretions up and right-down int HoneyCombMaze::VertexIndex(int u, int v) const { if (u <= 0) return ((3 * size_ + u) * (size_ + u - 1)) / 2 + v; else return (3 * size_ * (size_ - 1) + (4 * size_ - u - 1) * u) / 2 + v; } std::pair HoneyCombMaze::GetCenter(int u, int v) { double dxu = sqrt(3) / 2, dyu = 1.5, dxv = sqrt(3), dyv = 0; double cx = dxu * u + dxv * v, cy = dyu * u + dyv * v; return std::make_pair(cx, cy); } std::tuple HoneyCombMaze::GetEdge( int u, int v, int edge) const { double dxu = sqrt(3) / 2, dyu = 1.5, dxv = sqrt(3), dyv = 0; double cx = dxu * u + dxv * v, cy = dyu * u + dyv * v; double theta1 = (edge - 2.5) * M_PI / 3, theta2 = theta1 + M_PI / 3; return std::make_tuple(cx + cos(theta1), cy + sin(theta1), cx + cos(theta2), cy + sin(theta2)); } std::tuple HoneyCombMaze::GetCoordinateBounds() const { double xlim = sqrt(3) * (size_ - 0.5), ylim = 1.5 * size_ - 0.5; return std::make_tuple(-xlim, -ylim, xlim, ylim); } std::pair HoneyCombMaze::VExtent(int u) { if (u < 0) return {-size_ - u + 1, size_ - 1}; else return {-size_ + 1, size_ - 1 - u}; } bool HoneyCombMaze::IsValidNode(int u, int v) { if (u <= -size_ or u >= size_) return false; auto vextent = VExtent(u); return v >= vextent.first and v <= vextent.second; } ================================================ FILE: src/libs/mazes/src/kruskal.cpp ================================================ #include #include #include #include std::vector> Kruskal::SpanningTree( int vertices, const Graph& adjacencylist) { std::vector> edges; for (int i = 0; i < vertices; ++i) { for (const auto& edge : adjacencylist[i]) { if (edge.first > i) edges.push_back({i, edge.first}); } } shuffle(edges.begin(), edges.end(), generator); parent_ = std::vector(vertices); std::iota(parent_.begin(), parent_.end(), 0); spanningtree.clear(); for (const auto& edge : edges) { int u = GetParent(edge.first), v = GetParent(edge.second); if (u == v) continue; parent_[u] = v; spanningtree.push_back(edge); } return spanningtree; } int Kruskal::GetParent(int u) { return (parent_[u] == u) ? u : (parent_[u] = GetParent(parent_[u])); } ================================================ FILE: src/libs/mazes/src/looperasedrandomwalk.cpp ================================================ #include #include std::vector> LoopErasedRandomWalk::SpanningTree( int vertices, const Graph& adjacencylist) { spanningtree.clear(); visited = std::vector(vertices, 0); std::vector nodes(vertices); std::iota(nodes.begin(), nodes.end(), 0); shuffle(nodes.begin(), nodes.end(), generator); visited[nodes[0]] = 1; for (int round = 1, i = 1; i < vertices; ++i) { if (visited[nodes[i]]) continue; ++round; LERW(nodes[i], round, adjacencylist); } return spanningtree; } void LoopErasedRandomWalk::LERW(int vertex, int round, const Graph& adjacencylist) { std::vector current; while (!visited[vertex]) { visited[vertex] = round; current.push_back(vertex); int nextvertex; do { nextvertex = adjacencylist[vertex] [std::uniform_int_distribution( 0, adjacencylist[vertex].size() - 1)(generator)] .first; } while (nextvertex < 0); if (visited[nextvertex] == round) { // Erase the loop do { vertex = current.back(); visited[vertex] = 0; current.pop_back(); } while (vertex != nextvertex); } vertex = nextvertex; } current.push_back(vertex); for (unsigned int i = 0; i + 1 < current.size(); ++i) { spanningtree.push_back({current[i], current[i + 1]}); } } ================================================ FILE: src/libs/mazes/src/maze.cpp ================================================ #include #include #include #include Maze::Maze(int vertices, int startvertex, int endvertex) : vertices_(vertices), startvertex_(startvertex), endvertex_(endvertex) {} void Maze::InitialiseGraph() { adjacencylist_.clear(); adjacencylist_.resize(vertices_); cellCenters.resize(vertices_); } void Maze::GenerateMaze(SpanningtreeAlgorithm* algorithm) { auto spanningtree = algorithm->SpanningTree(vertices_, adjacencylist_); RemoveBorders(spanningtree); } void Maze::RemoveBorders(const std::vector>& edges) { for (const auto& edge : edges) { int u = edge.first, v = edge.second; for (int i = 0; i < (int)adjacencylist_[u].size(); ++i) { if (adjacencylist_[u][i].first == v) { adjacencylist_[u].erase(adjacencylist_[u].begin() + i); break; } } for (int i = 0; i < (int)adjacencylist_[v].size(); ++i) { if (adjacencylist_[v][i].first == u) { adjacencylist_[v].erase(adjacencylist_[v].begin() + i); break; } } } } void Maze::PrintMazeGnuplot(const std::string& outputprefix) const { std::ofstream gnuplotfile(outputprefix + ".plt"); if (!gnuplotfile) { std::cerr << "Error opening " << outputprefix << ".plt for writing.\n"; std::cerr << "Terminating."; exit(1); } gnuplotfile << "unset border\n"; gnuplotfile << "unset tics\n"; gnuplotfile << "set samples 15\n"; gnuplotfile << "set lmargin at screen 0\n"; gnuplotfile << "set rmargin at screen 1\n"; gnuplotfile << "set bmargin at screen 0\n"; gnuplotfile << "set tmargin at screen 1\n"; double xmin, ymin, xmax, ymax; std::tie(xmin, ymin, xmax, ymax) = GetCoordinateBounds(); gnuplotfile << "set xrange[" << xmin - 1 << ":" << xmax + 1 << "]\n"; gnuplotfile << "set yrange[" << ymin - 1 << ":" << ymax + 1 << "]\n"; int xresolution = (xmax - xmin + 2) * 30, yresolution = (ymax - ymin + 2) * 30; gnuplotfile << "set term pngcairo mono enhanced size " << xresolution << "," << yresolution << "\n"; gnuplotfile << "set output '" << outputprefix << ".png'\n"; gnuplotfile << "set multiplot\n"; for (int i = 0; i < vertices_; ++i) { for (const auto& edge : adjacencylist_[i]) { if (edge.first < i) gnuplotfile << edge.second->GnuplotPrintString() << "\n"; } } gnuplotfile << "plot 1/0 notitle\n"; gnuplotfile << "unset multiplot\n"; gnuplotfile << "set output\n"; } void Maze::PrintMazeSVG(const std::string& outputprefix) const { std::ofstream svgfile(outputprefix + ".svg"); if (!svgfile) { std::cerr << "Error opening " << outputprefix << ".svg for writing.\n"; std::cerr << "Terminating."; exit(1); } double xmin, ymin, xmax, ymax; std::tie(xmin, ymin, xmax, ymax) = GetCoordinateBounds(); int xresolution = (xmax - xmin + 2) * 30, yresolution = (ymax - ymin + 2) * 30; svgfile << "" << std::endl; svgfile << "" << std::endl; svgfile << "" << std::endl; for (int i = 0; i < vertices_; ++i) { for (const auto& edge : adjacencylist_[i]) { if (edge.first < i) { svgfile << edge.second->SVGPrintString() << "\n"; } } } svgfile << "" << std::endl; svgfile << "" << std::endl; } ================================================ FILE: src/libs/mazes/src/prim.cpp ================================================ #include #include std::vector> Prim::SpanningTree( int vertices, const Graph& adjacencylist) { spanningtree.clear(); PrimAlgorithm(vertices, adjacencylist); return spanningtree; } void Prim::PrimAlgorithm(int vertices, const Graph& adjacencylist) { std::vector visited(vertices, false); std::vector> boundary; int vertex = std::uniform_int_distribution(0, vertices - 1)(generator); for (int i = 1; i < vertices; ++i) { visited[vertex] = true; for (auto p : adjacencylist[vertex]) { if (p.first != -1 and !visited[p.first]) boundary.push_back({vertex, p.first}); } std::pair nextedge = {-1, -1}; do { int index = std::uniform_int_distribution(0, boundary.size() - 1)(generator); std::swap(boundary[index], boundary.back()); if (!visited[boundary.back().second]) nextedge = boundary.back(); boundary.pop_back(); } while (nextedge.first == -1); spanningtree.push_back(nextedge); vertex = nextedge.second; } } ================================================ FILE: src/libs/mazes/src/rectangularmaze.cpp ================================================ #include RectangularMaze::RectangularMaze(int width, int height) : Maze(width * height, 0, width * height - 1), width_(width), height_(height) {} int RectangularMaze::VertexIndex(int row, int column) { return row * width_ + column; } void RectangularMaze::InitialiseGraph() { Maze::InitialiseGraph(); // Lower and upper boundaries for (int i = 0; i < width_; ++i) { adjacencylist_[VertexIndex(0, i)].push_back( {-1, std::make_shared(i, 0, i + 1, 0)}); adjacencylist_[VertexIndex(height_ - 1, i)].push_back( {-1, std::make_shared(i, height_, i + 1, height_)}); } // Left and right boundaries, leaving space for entry and exit for (int i = 0; i < height_; ++i) { if (i != 0) adjacencylist_[VertexIndex(i, 0)].push_back( {-1, std::make_shared(0, i, 0, i + 1)}); if (i != height_ - 1) adjacencylist_[VertexIndex(i, 0)].push_back( {-1, std::make_shared(width_, i, width_, i + 1)}); } // Horizontally adjacent cells for (int i = 0; i < height_; ++i) { for (int j = 0; j < width_ - 1; ++j) { std::shared_ptr ptr = std::make_shared(j + 1, i, j + 1, i + 1); adjacencylist_[VertexIndex(i, j)].push_back({VertexIndex(i, j + 1), ptr}); adjacencylist_[VertexIndex(i, j + 1)].push_back({VertexIndex(i, j), ptr}); } } // Vertically adjacent cells for (int i = 0; i < height_ - 1; ++i) { for (int j = 0; j < width_; ++j) { std::shared_ptr ptr = std::make_shared(j, i + 1, j + 1, i + 1); adjacencylist_[VertexIndex(i, j)].push_back({VertexIndex(i + 1, j), ptr}); adjacencylist_[VertexIndex(i + 1, j)].push_back({VertexIndex(i, j), ptr}); } } } std::tuple RectangularMaze::GetCoordinateBounds() const { return std::make_tuple(0, 0, width_, height_); } ================================================ FILE: src/libs/mazes/src/spanningtreealgorithm.cpp ================================================ #include SpanningtreeAlgorithm::SpanningtreeAlgorithm() { generator = std::mt19937(randomdevice()); } ================================================ FILE: src/libs/mazes/src/usermaze.cpp ================================================ #include #include UserMaze::UserMaze(std::string filename) : filename_(filename) {} void UserMaze::InitialiseGraph() { Maze::InitialiseGraph(); std::ifstream in(filename_); in >> vertices_; startvertex_ = 0; endvertex_ = vertices_ - 1; adjacencylist_.clear(); adjacencylist_.resize(vertices_); xmin_ = std::numeric_limits::max(), ymin_ = xmin_; xmax_ = std::numeric_limits::min(), ymax_ = xmax_; int i, j; while (in >> i >> j) { std::string bordertype; in >> bordertype; if (bordertype == "Line") { double x1, x2, y1, y2; in >> x1 >> y1 >> x2 >> y2; xmax_ = std::max(xmax_, x2); ymax_ = std::max(ymax_, y2); xmin_ = std::min(xmin_, x1); ymin_ = std::min(ymin_, y1); adjacencylist_[i].push_back( {j, std::make_shared(x1, y1, x2, y2)}); } else if (bordertype == "Arc") { double cx, cy, r, theta1, theta2; in >> cx >> cy >> r >> theta1 >> theta2; xmax_ = std::max(xmax_, cx + r); ymax_ = std::max(ymax_, cy + r); xmin_ = std::min(xmin_, cx - r); ymin_ = std::min(ymin_, cy - r); adjacencylist_[i].push_back( {j, std::make_shared(cx, cy, r, theta1, theta2)}); } else continue; if (j != -1) adjacencylist_[j].push_back({i, adjacencylist_[i].back().second}); } } std::tuple UserMaze::GetCoordinateBounds() const { return std::make_tuple(xmin_, ymin_, xmax_, ymax_); } ================================================ FILE: src/libs/rendering/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.10) project(librendering VERSION 0.1 LANGUAGES CXX) add_library_default(rendering) target_link_libraries(rendering PUBLIC env Magnum::MeshTools Magnum::Primitives) ================================================ FILE: src/libs/rendering/include/rendering/render_utils.hpp ================================================ #pragma once #include #include #include #include namespace Megaverse { void initPrimitives(std::map &meshData); class Overview { public: void saveTransformation() { rootTransformation = root->transformation(); verticalTiltTransformation = verticalTilt->transformation(); } void restoreTransformation() const { root->setTransformation(rootTransformation); verticalTilt->setTransformation(verticalTiltTransformation); } void reset(Object3D *parent); public: Object3D *root{}, *verticalTilt{}; Magnum::SceneGraph::Camera3D *camera{}; bool enabled = false; float verticalRotation = 0.0f; Magnum::Matrix4 rootTransformation{}, verticalTiltTransformation{}; }; } ================================================ FILE: src/libs/rendering/src/render_utils.cpp ================================================ #include #include #include #include #include #include #include #include #include #include using namespace Megaverse; using namespace Magnum; using namespace Magnum::Math::Literals; void Megaverse::initPrimitives(std::map &meshes) { auto &m = meshes; // type less m.emplace(DrawableType::Box, Primitives::cubeSolid()); m.emplace(DrawableType::Capsule, Primitives::capsule3DSolid(3, 3, 8, 1.0)); m.emplace(DrawableType::Sphere, Primitives::icosphereSolid(1)); m.emplace(DrawableType::Cone, Primitives::coneSolid(1, 6, 0.5f)); m.emplace(DrawableType::Cylinder, Primitives::cylinderSolid(1, 6, 0.5f, Magnum::Primitives::CylinderFlag::CapEnds)); } void Overview::reset(Object3D *parent) { root = &parent->addChild(); root->rotateYLocal(225.0_degf); root->translateLocal(Magnum::Vector3{0.1f, 20.0f, 0.1f}); verticalTilt = &root->addChild(); verticalTilt->rotateXLocal(-40.0_degf); verticalRotation = -40.0f; camera = &(verticalTilt->addFeature()); auto [fov, near, far, aspectRatio] = overviewCameraParameters(); camera->setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend) .setProjectionMatrix(Matrix4::perspectiveProjection(Deg(fov), aspectRatio, near, far)) .setViewport(GL::defaultFramebuffer.viewport().size()); if (rootTransformation != Matrix4{} || verticalTiltTransformation != Matrix4{}) restoreTransformation(); else saveTransformation(); } ================================================ FILE: src/libs/scenarios/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.10) project(libscenarios VERSION 0.1 LANGUAGES CXX) add_library_default(scenarios) target_link_libraries(scenarios PUBLIC env util mazes) ================================================ FILE: src/libs/scenarios/include/scenarios/component_fall_detection.hpp ================================================ #pragma once #include namespace Megaverse { class FallDetectionCallbacks { public: virtual ~FallDetectionCallbacks() = default; virtual void agentFell(int /*agentIdx*/) {} }; template class FallDetectionComponent : public ScenarioComponent { public: explicit FallDetectionComponent(Scenario &scenario, VoxelGrid &grid, FallDetectionCallbacks &callbacks, int fallThreshold = -20) : ScenarioComponent{scenario} , grid{grid} , callbacks{callbacks} , fallThreshold{fallThreshold} { } void reset(Env &, Env::EnvState &) override { agentInitialPositions.clear(); } void step(Env &env, Env::EnvState &envState) override { for (int i = 0; i < env.getNumAgents(); ++i) { auto &a = envState.agents[i]; if (a->absoluteTransformation().translation().y() < fallThreshold) { resetAgent(i, a); callbacks.agentFell(i); } } } void resetAgent(int agentIdx, AbstractAgent *a) { auto p = agentInitialPositions[agentIdx]; auto v = grid.getWithVector(p); while (v && !v->empty() && p.y() < 1000) { ++p.y(); v = grid.getWithVector(p); } const float halfVoxel = grid.getVoxelSize() / 2; a->teleport(btVector3{p.x() + halfVoxel, p.y() + halfVoxel, p.z() + halfVoxel}); } public: VoxelGrid &grid; std::vector agentInitialPositions; FallDetectionCallbacks &callbacks; int fallThreshold = -20; }; } ================================================ FILE: src/libs/scenarios/include/scenarios/component_hexagonal_maze.hpp ================================================ #pragma once #include class HoneyCombMaze; namespace Megaverse { class HexagonalMazeComponent : public ScenarioComponent { public: explicit HexagonalMazeComponent(Scenario &scenario); ~HexagonalMazeComponent() override; void reset(Env &, Env::EnvState &) override; void addDrawablesAndCollisions(DrawablesMap &drawables, Env::EnvState &envState) const; HoneyCombMaze & getMaze() const { return *maze; } [[nodiscard]] float getScale() const { return mazeScale; } [[nodiscard]] int getSize() const { return mazeSize; } public: int minSize = 2, maxSize = 10; float omitWallsProbabilityMin = 0.1f, omitWallsProbabilityMax = 0.7f; private: int mazeSize = 0; float mazeScale = 1.0f; float wallHeight = 1.0f; float omitWallsProbability = 0.0f; ColorRgb bottomEdgingColor, topEdgingColor; float wallLandmarkProbability = 0.0f; std::unique_ptr maze; double xMin = 0, xMax = 0, yMin = 0, yMax = 0; }; } ================================================ FILE: src/libs/scenarios/include/scenarios/component_object_stacking.hpp ================================================ #pragma once #include namespace Megaverse { struct VoxelWithPhysicsObjects : public VoxelState { RigidBody *physicsObject = nullptr; }; class ObjectStackingCallbacks { public: virtual ~ObjectStackingCallbacks() = default; virtual bool canPlaceObject(int /*agentIdx*/, const VoxelCoords &, Object3D * /*obj*/) { return true; } virtual void placedObject(int /*agentIdx*/, const VoxelCoords &, Object3D * /*obj*/) {} virtual void pickedObject(int /*agentIdx*/, const VoxelCoords &, Object3D * /*obj*/) {} }; /** * @tparam VoxelT has to have the field "physicsObject" * SFINAE check or C++20 concepts check would be nice here. */ template class ObjectStackingComponent : public ScenarioComponent { public: explicit ObjectStackingComponent(Scenario &scenario, int numAgents, VoxelGrid &grid, ObjectStackingCallbacks &callbacks) : ScenarioComponent{scenario} , grid{grid} , carryingObject(size_t(numAgents), nullptr) , callbacks{callbacks} { } void reset(Env &, Env::EnvState &) override { std::fill(carryingObject.begin(), carryingObject.end(), nullptr); } void step(Env &env, Env::EnvState &envState) override { for (int i = 0; i < env.getNumAgents(); ++i) { const auto a = envState.currAction[i]; if (!!(a & Action::Interact)) onInteractAction(i, envState); } } Object3D * agentCarryingObject(int agentIdx) const { return carryingObject[agentIdx]; } void onInteractAction(int agentIdx, Env::EnvState &envState) { AbstractAgent *agent = envState.agents[agentIdx]; const auto carryingScale = 0.78f, carryingScaleInverse = 1.0f / carryingScale; // putting object on the ground if (carryingObject[agentIdx]) { auto obj = carryingObject[agentIdx]; const auto t = obj->absoluteTransformation().translation(); VoxelCoords voxel = grid.getCoords(t); auto voxelPtr = grid.get(voxel); bool collidesWithAgent = false; for (const auto a : envState.agents) { if (a == agent) continue; const auto agentTransformation = a->transformation().translation(); VoxelCoords c = grid.getCoords(agentTransformation); if (voxel == c) { collidesWithAgent = true; break; } } const bool empty = !voxelPtr || (voxelPtr->empty() && !voxelPtr->physicsObject); if (empty && !collidesWithAgent && callbacks.canPlaceObject(agentIdx, voxel, obj)) { // voxel in front of us is empty, can place the object // the object should be on the ground or on top of another object // descend on y axis until we find ground while (true) { VoxelCoords voxelBelow{voxel.x(), voxel.y() - 1, voxel.z()}; if (voxelBelow.y() < -30) { // this is the lowest level we support break; } auto voxelBelowPtr = grid.get(voxelBelow); if (voxelBelowPtr && (voxelBelowPtr->solid() || voxelBelowPtr->physicsObject)) break; else voxel = voxelBelow; } // placing object on the ground (or another object) if (!grid.hasVoxel(voxel)) { VoxelT voxelState; grid.set(voxel, voxelState); } grid.get(voxel)->physicsObject = obj; obj->setParent(envState.scene.get()); auto scaling = obj->transformation().scaling(); obj->resetTransformation(); obj->scale({scaling.x() * carryingScaleInverse, scaling.y() * carryingScaleInverse, scaling.z() * carryingScaleInverse}); obj->translate({float(voxel.x()) + 0.5f, float(voxel.y()) + 0.5f, float(voxel.z()) + 0.5f}); obj->syncPose(); obj->toggleCollision(); carryingObject[agentIdx] = nullptr; callbacks.placedObject(agentIdx, voxel, obj); } } else { // picking up an object const auto pickup = agent->interactLocation()->absoluteTransformation().translation(); VoxelCoords voxel = lround(floor(pickup)); VoxelCoords voxelAbove{voxel.x(), voxel.y() + 1, voxel.z()}; int pickupHeight = 0, maxPickupHeight = 1; while (pickupHeight <= maxPickupHeight) { auto voxelPtr = grid.get(voxel), voxelAbovePtr = grid.get(voxelAbove); bool hasObjectAbove = voxelAbovePtr && voxelAbovePtr->physicsObject; if (voxelPtr && voxelPtr->physicsObject && !hasObjectAbove) { auto obj = voxelPtr->physicsObject; obj->toggleCollision(); obj->setParent(agent); auto scaling = obj->transformation().scaling(); obj->resetTransformation(); obj->scale({scaling.x() * carryingScale, scaling.y() * carryingScale, scaling.z() * carryingScale}); obj->translate({0.0f, -0.3f, 0.0f}); obj->setParent(agent->interactLocation()); carryingObject[agentIdx] = obj; voxelPtr->physicsObject = nullptr; callbacks.pickedObject(agentIdx, voxel, obj); break; } else { voxel = voxelAbove; voxelAbove = VoxelCoords{voxel.x(), voxel.y() + 1, voxel.z()}; } ++pickupHeight; } } } void addDrawablesAndCollisions(DrawablesMap &drawables, Env::EnvState &envState, const std::vector &objectPositions) { const auto objSize = 0.39f; auto objScale = Magnum::Vector3{objSize, objSize, objSize}; for (const auto &movableObject : objectPositions) { const auto pos = movableObject; auto translation = Magnum::Vector3{float(pos.x()) + 0.5f, float(pos.y()) + 0.5f, float(pos.z()) + 0.5f}; auto bBoxShape = std::make_unique(btVector3{1, 1, 1}); auto &object = envState.scene->addChild(envState.scene.get(), 0.0f, bBoxShape.get(), envState.physics->bWorld); object.scale(objScale).translate(translation); object.setCollisionScale({1.15f, 1.15f, 1.15f}); object.setCollisionOffset({0, -0.05f, 0}); object.syncPose(); drawables[DrawableType::Box].emplace_back(&object, rgb(ColorRgb::MOVABLE_BOX)); envState.physics->collisionShapes.emplace_back(std::move(bBoxShape)); if (!grid.hasVoxel(pos)) { VoxelT voxelState; grid.set(pos, voxelState); } grid.get(pos)->physicsObject = &object; } } private: VoxelGrid &grid; std::vector carryingObject; ObjectStackingCallbacks &callbacks; }; } ================================================ FILE: src/libs/scenarios/include/scenarios/component_platforms.hpp ================================================ #pragma once #include #include namespace Megaverse { class PlatformsComponent : public ScenarioComponent { public: explicit PlatformsComponent(Scenario &scenario) : ScenarioComponent{scenario} { } void reset(Env &, Env::EnvState &) override { platforms.clear(); levelRoot.reset(); levelRoot = std::make_unique(nullptr); } void addPlatform(std::unique_ptr platform) { platforms.emplace_back(std::move(platform)); } public: std::vector> platforms; std::unique_ptr levelRoot; }; } ================================================ FILE: src/libs/scenarios/include/scenarios/component_voxel_grid.hpp ================================================ #pragma once #include #include #include #include namespace Megaverse { struct CoordRange { int min, max; }; inline CoordRange startEndCoord(int bboxMin, int bboxMax, int direction) { if (direction == 1) return {bboxMax + 1, bboxMax + 1}; else if (direction == -1) return {bboxMin - 1, bboxMin - 1}; else return {bboxMin, bboxMax}; } using Boxes = std::vector; struct BBoxInfo { public: BBoxInfo() = default; BBoxInfo(uint8_t type, ColorRgb color) : type{type} , color{color} {} bool operator <(const BBoxInfo &info) const { return type == info.type ? color < info.color : type < info.type; } public: uint8_t type{}; ColorRgb color{}; }; // comment this to disable voxel layout optimization, i.e. for ablation study #define OPTIMIZE_VOXEL_LAYOUT /** * Environments that use voxel grids for layouts or runtime checks should include this component. * @tparam VoxelT data stored in each non-empty voxel cell. */ template class VoxelGridComponent : public ScenarioComponent { public: explicit VoxelGridComponent(Scenario &scenario, int maxVoxelsXYZ = 100, float minX = 0, float minY = 0, float minZ = 0, float voxelSize = 1) : ScenarioComponent{scenario} , grid{size_t(maxVoxelsXYZ), {minX, minY, minZ}, voxelSize} { } void reset(Env &, Env::EnvState &) override { grid.clear(); } void addPlatform(const Platform &p, ColorRgb layoutColor, ColorRgb wallColor, bool drawWalls = true) { for (auto &bb : p.layoutBoxes) addBoundingBox(bb.boundingBox(), VoxelState::generateType(true, true), TERRAIN_NONE, layoutColor); for (auto &bb : p.wallBoxes) addBoundingBox(bb.boundingBox(), VoxelState::generateType(true, drawWalls), TERRAIN_NONE, wallColor); for (auto &[terrainType, v] : p.terrainBoxes) for (auto &bb : v) addTerrainBoundingBox(bb.boundingBox(), terrainType); } template void addBoundingBox(const BoundingBox &bb, Args&&... args) { for (int x = bb.min.x(); x < bb.max.x(); ++x) for (int y = bb.min.y(); y < bb.max.y(); ++y) for (int z = bb.min.z(); z < bb.max.z(); ++z) grid.set({x, y, z}, makeVoxel(std::forward(args)...)); } template void addTerrainBoundingBox(const BoundingBox &bb, int terrain) { for (int x = bb.min.x(); x < bb.max.x(); ++x) for (int y = bb.min.y(); y < bb.max.y(); ++y) for (int z = bb.min.z(); z < bb.max.z(); ++z) { const VoxelCoords coords{x, y, z}; if (!grid.hasVoxel(coords)) grid.set({x, y, z}, VoxelT()); grid.get(coords)->terrain |= terrain; } } std::map toBoundingBoxes() { const static Magnum::Vector3i directions[] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; std::unordered_set visited; const auto gridHashMap = grid.getHashMap(); std::map boxesByVoxelType; for (auto it : gridHashMap) { const auto &coord = it.first; const auto &voxel = it.second; const auto voxelType = voxel.voxelType; const auto color = voxel.color; if (visited.count(coord)) { // already processed this voxel continue; } visited.emplace(coord); BoundingBox bbox{coord, coord}; std::vector expansion; #ifdef OPTIMIZE_VOXEL_LAYOUT // try to expand the parallelepiped in every direction as far as we can for (auto direction : directions) { for (int sign = -1; sign <= 1; sign += 2) { auto d = direction * sign; bool canExpand = true; // expanding in a specific direction as far as we can while (true) { const auto xlim = startEndCoord(bbox.min.x(), bbox.max.x(), d.x()); const auto ylim = startEndCoord(bbox.min.y(), bbox.max.y(), d.y()); const auto zlim = startEndCoord(bbox.min.z(), bbox.max.z(), d.z()); expansion.clear(); for (auto x = xlim.min; x <= xlim.max; ++x) for (auto y = ylim.min; y <= ylim.max; ++y) for (auto z = zlim.min; z <= zlim.max; ++z) { const VoxelCoords coords{x, y, z}; const auto v = grid.get(coords); if (!v || v->voxelType != voxelType || v->color != color || visited.count(coords)) { // we could not expand in this direction canExpand = false; goto afterLoop; } expansion.emplace_back(coords); } afterLoop: if (!canExpand) break; for (auto newVoxelCoord : expansion) { visited.emplace(newVoxelCoord); bbox.addPoint(newVoxelCoord); } } } } #else UNUSED(directions); #endif // finished expanding in all possible directions // the bounding box defines the parallepiped completely filled by solid voxels // we can draw only this parallelepiped (8 vertices) instead of drawing individual voxels, saving a ton of time boxesByVoxelType[{voxelType, color}].emplace_back(bbox); } return boxesByVoxelType; } public: VoxelGrid grid; }; } ================================================ FILE: src/libs/scenarios/include/scenarios/const.hpp ================================================ #pragma once #include namespace Megaverse::Str { ConstStr obstaclesMinNumPlatforms = "obstaclesMinNumPlatforms", obstaclesMaxNumPlatforms = "obstaclesMaxNumPlatforms", obstaclesMinGap = "obstaclesMinGap", obstaclesMaxGap = "obstaclesMaxGap", obstaclesMinLava = "obstaclesMinLava", obstaclesMaxLava = "obstaclesMaxLava", obstaclesMinHeight = "obstaclesMinHeight", obstaclesMaxHeight = "obstaclesMaxHeight", obstaclesAgentAtExit = "obstaclesAgentAtExit", obstaclesAllAgentsAtExit = "obstaclesAllAgentsAtExit", obstaclesExtraReward = "obstaclesExtraReward", obstaclesNumAllowedMaxDifficulty = "obstaclesNumAllowedMaxDifficulty", obstaclesAgentCarriedObjectToExit = "obstaclesAgentCarriedObjectToExit"; ConstStr towerPickedUpObject = "towerPickedUpObject", towerVisitedBuildingZoneWithObject = "towerVisitedBuildingZoneWithObject", towerBuildingReward = "towerBuildingReward"; ConstStr collectSingleGood = "collectSingleGood", collectSingleBad = "collectSingleBad", collectAll = "collectAll", collectAbyss = "collectAbyss"; ConstStr sokobanBoxOnTarget = "sokobanBoxOnTarget", sokobanBoxLeavesTarget = "sokobanBoxLeavesTarget", sokobanAllBoxesOnTarget = "sokobanAllBoxesOnTarget"; ConstStr boxagoneTouchedFloor = "boxagoneTouchedFloor", boxagonePerStepReward = "boxagonePerStepReward"; ConstStr exploreSolved = "exploreSolved"; ConstStr memoryCollectGood = "memoryCollectGood", memoryCollectBad = "memoryCollectBad"; ConstStr rearrangeOneMoreObjectCorrectPosition = "rearrangeOneMoreObjectCorrectPosition", rearrangeAllObjectsCorrectPosition = "rearrangeAllObjectsCorrectPosition"; } ================================================ FILE: src/libs/scenarios/include/scenarios/init.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace Megaverse { template void registerScenario(const std::string &name) { Scenario::registerScenario(name, Scenario::scenarioFactory); } void scenariosGlobalInit() { static bool initialized = false; if (initialized) return; initialized = true; // used for debugging and testing registerScenario("Empty"); registerScenario("Test"); // experimental registerScenario("Football"); registerScenario("BoxAGone"); // "main" envs registerScenario("TowerBuilding"); registerScenario("ObstaclesEasy"); registerScenario("ObstaclesHard"); registerScenario("Collect"); registerScenario("Sokoban"); registerScenario("HexMemory"); registerScenario("HexExplore"); registerScenario("Rearrange"); registerScenario("ObstaclesMedium"); // auxiliary obstacle scenarios, for "curriculum" registerScenario("ObstaclesWalls"); registerScenario("ObstaclesSteps"); registerScenario("ObstaclesLava"); } } ================================================ FILE: src/libs/scenarios/include/scenarios/layout_utils.hpp ================================================ #pragma once #include #include #include namespace Megaverse { template void addDrawablesAndCollisionObjectsFromVoxelGrid(VoxelGridComponent &vg, DrawablesMap &drawables, Env::EnvState &envState, float voxelSize) { auto boundingBoxesByType = vg.toBoundingBoxes(); for (auto &[bbInfo, bb] : boundingBoxesByType) addBoundingBoxes(drawables, envState, bb, bbInfo.type, bbInfo.color, voxelSize); } void addBoundingBoxes(DrawablesMap &drawables, Env::EnvState &envState, const Boxes &boxes, int voxelType, ColorRgb color, float voxelSize); void addTerrain(DrawablesMap &drawables, Env::EnvState &envState, TerrainType type, const BoundingBox &bb, float voxelSize = 1.0f); void addStaticCollidingBox( DrawablesMap &drawables, Env::EnvState &envState, Magnum::Vector3 scale, Magnum::Vector3 translation, ColorRgb color ); Object3D * addCylinder(DrawablesMap &drawables, Object3D &parent, Magnum::Vector3 translation, Magnum::Vector3 scale, ColorRgb color); Object3D * addSphere(DrawablesMap &drawables, Object3D &parent, Magnum::Vector3 translation, Magnum::Vector3 scale, ColorRgb color); Object3D * addPillar(DrawablesMap &drawables, Object3D &parent, Magnum::Vector3 translation, Magnum::Vector3 scale, ColorRgb color); Object3D * addDiamond(DrawablesMap &drawables, Object3D &parent, Magnum::Vector3 translation, Magnum::Vector3 scale, ColorRgb color); } ================================================ FILE: src/libs/scenarios/include/scenarios/platforms.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include namespace Megaverse { enum PlatformOrientation { ORIENTATION_STRAIGHT, ORIENTATION_TURN_LEFT, ORIENTATION_TURN_RIGHT, }; enum TerrainType { TERRAIN_NONE = 0, TERRAIN_EXIT = 1, TERRAIN_LAVA = 1 << 1, TERRAIN_BUILDING_ZONE = 1 << 2, }; enum { WALLS_SOUTH = 1, WALLS_NORTH = 1 << 1, WALLS_WEST = 1 << 2, WALLS_EAST = 1 << 3, WALLS_NONE = 0, WALLS_ALL = WALLS_SOUTH | WALLS_NORTH | WALLS_EAST | WALLS_WEST, }; inline ColorRgb terrainColor(TerrainType type) { const static std::map colors = { {TerrainType::TERRAIN_EXIT, ColorRgb::EXIT_PAD}, {TerrainType::TERRAIN_LAVA, ColorRgb::RED}, {TerrainType::TERRAIN_BUILDING_ZONE, ColorRgb::BUILDING_ZONE}, }; return colors.at(type); } struct BoundingBox { BoundingBox() = default; BoundingBox(const VoxelCoords &min, const VoxelCoords &max) : min{min}, max{max} { } BoundingBox(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) : min{minX, minY, minZ} , max{maxX, maxY, maxZ} { } void addPoint(const VoxelCoords &v) { if (v.x() < min.x()) min.x() = v.x(); if (v.x() > max.x()) max.x() = v.x(); if (v.y() < min.y()) min.y() = v.y(); if (v.y() > max.y()) max.y() = v.y(); if (v.z() < min.z()) min.z() = v.z(); if (v.z() > max.z()) max.z() = v.z(); } void sort() { // sort the min/max vertices after the transformation // this only works for rotations in 90-degree increments if (min.x() > max.x()) std::swap(min.x(), max.x()); if (min.y() > max.y()) std::swap(min.y(), max.y()); if (min.z() > max.z()) std::swap(min.z(), max.z()); } [[nodiscard]] bool collidesWith(const BoundingBox &other) const { // we're looking for an axis with no overlap if (max.x() <= other.min.x()) return false; if (min.x() >= other.max.x()) return false; if (max.y() <= other.min.y()) return false; if (min.y() >= other.max.y()) return false; if (max.z() <= other.min.z()) return false; if (min.z() >= other.max.z()) return false; return true; } public: VoxelCoords min, max; }; struct MagnumAABB { MagnumAABB(Object3D &parent, const BoundingBox &bb) : min(&parent.addChild()) , max(&parent.addChild()) { min->translateLocal(Magnum::Vector3(bb.min)); max->translateLocal(Magnum::Vector3(bb.max)); } [[nodiscard]] BoundingBox boundingBox() const { BoundingBox bb; bb.min = Magnum::Math::lround(min->absoluteTransformation().translation()); bb.max = Magnum::Math::lround(max->absoluteTransformation().translation()); bb.sort(); return bb; } public: Object3D *min, *max; }; class Platform { public: explicit Platform(Object3D *parent, Rng &rng, int walls, const FloatParams ¶ms) : rng{rng} , walls{walls} , root{&parent->addChild()} , params{params} { } virtual ~Platform() = default; virtual void init() = 0; virtual void generate() = 0; virtual void rotateCCW(int /*previousPlatformWidth*/) { root->rotateYLocal(degrees(90.0)); root->translateLocal({-1, 0, -1}); } virtual void rotateCW(int previousPlatformWidth) { root->rotateYLocal(degrees(-90.0)); root->translateLocal({float(previousPlatformWidth) - 1, 0, -float(width) + 1}); } virtual Object3D * anchorPoint() { return nextPlatformAnchor; } virtual void addFloor() { MagnumAABB floor{*root, {0, 0, 0, length, 1, width}}; layoutBoxes.emplace_back(floor); nextPlatformAnchor = &root->addChild(); nextPlatformAnchor->translateLocal({float(length), 0, 0}); boundingBoxDirty = true; } virtual void addWalls() { if (walls & WALLS_SOUTH) wallBoxes.emplace_back(MagnumAABB{*root, {0, 0, 0, 1, height, width}}); if (walls & WALLS_NORTH) wallBoxes.emplace_back(MagnumAABB{*root, {length - 1, 0, 0, length, height, width}}); if (walls & WALLS_EAST) wallBoxes.emplace_back(MagnumAABB{*root, {0, 0, 0, length, height, 1}}); if (walls & WALLS_WEST) wallBoxes.emplace_back(MagnumAABB{*root, {0, 0, width - 1, length, height, width}}); boundingBoxDirty = true; } virtual BoundingBox platformBoundingBox() { if (!boundingBoxDirty) return outerBoundingBox; if (!layoutBoxes.empty()) outerBoundingBox = layoutBoxes.front().boundingBox(); else if (!wallBoxes.empty()) outerBoundingBox = wallBoxes.front().boundingBox(); else outerBoundingBox = BoundingBox{}; boundingBoxDirty = false; for (auto &boxes : {layoutBoxes, wallBoxes}) for (auto &box : boxes) { const auto bb = box.boundingBox(); outerBoundingBox.addPoint(bb.min); outerBoundingBox.addPoint(bb.max); } return outerBoundingBox; } virtual bool collidesWith(Platform &other) { return platformBoundingBox().collidesWith(other.platformBoundingBox()); } virtual std::vector agentSpawnPoints(int numAgents) { std::vector spawnPoints; std::set> used; for (int i = 0; i < numAgents; ++i) { int x, y, z; for (int attempt = 0; attempt < 10; ++attempt) { x = randRange(1, length - 1, rng); z = randRange(1, width - 1, rng); if (used.count({x, z})) continue; y = occupancy[{x, z}] + 1; occupancy[{x, z}] += 2; spawnPoints.emplace_back(x, y, z); used.emplace(x, z); break; } } return spawnPoints; } virtual int requiresMovableBoxesToTraverse() { return 0; } virtual std::vector generateObjectPositions(int numPositionsToGenerate) { std::vector boxes; constexpr int maxAttempts = 10; for (int i = 0; i < numPositionsToGenerate; ++i) { for (int attempt = 0; attempt < 10; ++attempt) { const int x = randRange(1, length - 1, rng); const int z = randRange(1, width - 1, rng); if (occupancy[{x, z}] < 2 || attempt >= maxAttempts - 1) { const int y = ++occupancy[{x, z}]; boxes.emplace_back(x, y, z); break; } } } return adjustTransformation(boxes); } std::vector adjustTransformation(std::vector &coords) const { for (auto &c : coords) { Magnum::Vector3 v{c.x() + 0.5f, c.y() + 0.5f, c.z() + 0.5f}; c = toVoxel(root->absoluteTransformation().transformPoint(v)); } return coords; } int param(const std::string &str) const { return int(lround(params.at(str))); } virtual bool isMaxDifficulty() const { return false; } public: Rng &rng; int walls{}; // length = x, height = y, width = z int length{}, height{}, width{}; std::vector layoutBoxes, wallBoxes; std::map> terrainBoxes; Object3D *nextPlatformAnchor{}, *root{}; bool boundingBoxDirty = true; BoundingBox outerBoundingBox; std::map, int> occupancy; const FloatParams ¶ms; }; class EmptyPlatform : public Platform { public: explicit EmptyPlatform(Object3D *parent, Rng &rng, int walls, const FloatParams ¶ms, int w = -1) : Platform{parent, rng, walls, params} { width = w; } void init() override { length = randRange(4, 10, rng); if (width == -1) width = randRange(5, 9, rng); // height = randRange(3, 7, rng); height = 5; } void generate() override { addFloor(); addWalls(); } }; class WallPlatform : public EmptyPlatform { public: explicit WallPlatform(Object3D *parent, Rng &rng, int walls, const FloatParams ¶ms, int w = -1) : EmptyPlatform{parent, rng, walls, params, w} { } void init() override { EmptyPlatform::init(); wallHeight = randRange(param(Str::obstaclesMinHeight), param(Str::obstaclesMaxHeight) + 1, rng); height = randRange(wallHeight + 4, wallHeight + 6, rng); } void generate() override { EmptyPlatform::generate(); const auto wallX = randRange(1, length, rng); const auto wallThickness = randRange(1, length - wallX + 1, rng); MagnumAABB wall{*root, {wallX, 1, 1, wallX + wallThickness, 1 + wallHeight, width - 1}}; layoutBoxes.emplace_back(wall); // this way we won't generate boxes on top of the wall (for the most part) for (int x = wallX; x < wallX + wallThickness; ++x) for (int z = 1; z < width; ++z) occupancy[{x, z}] = wallHeight; } int requiresMovableBoxesToTraverse() override { return triangularNumber(wallHeight - 1); } bool isMaxDifficulty() const override { return wallHeight >= param(Str::obstaclesMaxHeight); } private: int wallHeight{}; }; class LavaPlatform : public EmptyPlatform { public: explicit LavaPlatform(Object3D *parent, Rng &rng, int walls, const FloatParams ¶ms, int w = -1) : EmptyPlatform{parent, rng, walls, params, w} { } void init() override { EmptyPlatform::init(); length = randRange(6, 12, rng); auto minLava = std::min(param(Str::obstaclesMinLava), length - 2); auto maxLava = std::min(param(Str::obstaclesMaxLava) + 1, length - 1); lavaLength = randRange(minLava, maxLava, rng); } void generate() override { EmptyPlatform::generate(); const auto lavaX = randRange(1, length - lavaLength, rng); MagnumAABB lava{*root, {lavaX, 1, 1, lavaX + lavaLength, 2, width - 1}}; terrainBoxes[TERRAIN_LAVA].emplace_back(lava); } int requiresMovableBoxesToTraverse() override { return std::max(1, lavaLength - 1); } bool isMaxDifficulty() const override { return lavaLength >= param(Str::obstaclesMaxLava); } private: int lavaLength{}; }; class StepPlatform : public EmptyPlatform { public: explicit StepPlatform(Object3D *parent, Rng &rng, int walls, const FloatParams ¶ms, int w = -1) : EmptyPlatform{parent, rng, walls, params, w} { } void init() override { EmptyPlatform::init(); stepHeight = randRange(param(Str::obstaclesMinHeight), param(Str::obstaclesMaxHeight) + 1, rng); height = randRange(stepHeight + 2, stepHeight + 5, rng); } void generate() override { const auto stepX = randRange(1, length, rng); MagnumAABB floorLow{*root, {0, 0, 0, stepX + 1, 1, width}}; MagnumAABB floorHigh{*root, {stepX, stepHeight, 0, length, stepHeight + 1, width}}; MagnumAABB wall{*root, {stepX, 0, 0, stepX + 1, stepHeight + 1, width}}; layoutBoxes.emplace_back(floorLow); layoutBoxes.emplace_back(floorHigh); layoutBoxes.emplace_back(wall); nextPlatformAnchor = &root->addChild(); nextPlatformAnchor->translateLocal({float(length), float(stepHeight), 0}); addWalls(); // this way we won't generate boxes on top of the step (for the most part) for (int x = stepX + 1; x < length; ++x) for (int z = 1; z < width; ++z) occupancy[{x, z}] = stepHeight; } int requiresMovableBoxesToTraverse() override { return triangularNumber(stepHeight - 1); } bool isMaxDifficulty() const override { return stepHeight >= param(Str::obstaclesMaxHeight); } private: int stepHeight{}; }; class GapPlatform : public EmptyPlatform { public: explicit GapPlatform(Object3D *parent, Rng &rng, int walls, const FloatParams ¶ms, int w = -1) : EmptyPlatform{parent, rng, walls, params, w} { } void init() override { EmptyPlatform::init(); gap = randRange(param(Str::obstaclesMinGap), std::min(param(Str::obstaclesMaxGap) + 1, length - 1), rng); gapX = randRange(1, length - gap, rng); } void generate() override { MagnumAABB floorStart{*root, {0, 0, 0, gapX, 1, width}}; MagnumAABB floorEnd{*root, {gapX + gap, 0, 0, length, 1, width}}; layoutBoxes.emplace_back(floorStart); layoutBoxes.emplace_back(floorEnd); nextPlatformAnchor = &root->addChild(); nextPlatformAnchor->translateLocal({float(length), 0, 0}); addWalls(); } int requiresMovableBoxesToTraverse() override { return triangularNumber(std::max(0, gap - 2)); } std::vector generateObjectPositions(int numPositionsToGenerate) override { std::vector boxes, candidates; for (int x = 0; x < length; ++x) for (int z = 1; z < width - 1; ++z) { if (x >= gapX && x < gapX + gap) continue; candidates.emplace_back(x, 1, z); } for (int i = 0; i < numPositionsToGenerate; ++i) { const auto v = randomSample(candidates, rng); const int y = ++occupancy[{v.x(), v.z()}]; boxes.emplace_back(v.x(), y, v.z()); } return adjustTransformation(boxes); } private: int gap{}, gapX{}; }; class StartPlatform : public EmptyPlatform { public: explicit StartPlatform(Object3D *parent, Rng &rng, const FloatParams ¶ms, int w = -1) : EmptyPlatform{parent, rng, WALLS_SOUTH | WALLS_EAST | WALLS_WEST, params, w} { } void init() override { EmptyPlatform::init(); } }; class ExitPlatform : public EmptyPlatform { public: explicit ExitPlatform(Object3D *parent, Rng &rng, const FloatParams ¶ms, int w = -1) : EmptyPlatform{parent, rng, WALLS_NORTH | WALLS_EAST | WALLS_WEST, params, w} { } void generate() override { EmptyPlatform::generate(); MagnumAABB exit{*root, {length - 3, 1, 1, length - 1, 3, width - 1}}; terrainBoxes[TERRAIN_EXIT].emplace_back(exit); } }; class TransitionPlatform : public EmptyPlatform { public: explicit TransitionPlatform(Object3D *parent, Rng &rng, int walls, const FloatParams ¶ms, int l, int w) : EmptyPlatform{parent, rng, walls, params, -1} { length = l, width = w; } void init() override { height = 5; } }; } ================================================ FILE: src/libs/scenarios/include/scenarios/scenario_box_a_gone.hpp ================================================ #pragma once #include #include #include #include #include #include namespace Megaverse { struct VoxelBoxAGone : public VoxelState { RigidBody *disappearingPlatform = nullptr; } __attribute__((aligned(8))); class BoxAGoneScenario : public DefaultScenario, public FallDetectionCallbacks { private: class BoxAGonePlatform; struct AgentState { RigidBody *lastPlatform = nullptr; float secondsBeforeTouchedFloor = 0.0f; }; struct PlatformState { int remainingTicks = 0; VoxelCoords coords; RigidBody *temporaryPlatform = nullptr; } __attribute__((aligned(32))); public: explicit BoxAGoneScenario(const std::string &name, Env &env, Env::EnvState &envState); ~BoxAGoneScenario() override; // Scenario interface void reset() override; void step() override; std::vector agentStartingPositions() override; void addEpisodeDrawables(DrawablesMap &drawables) override; void addDisappearingPlatforms(DrawablesMap &drawables); /** * Different trueObjective depending on whether this is a single-agent or competitive setting. */ float trueObjective(int agentIdx) const override { if (env.getNumAgents() > 1) { // last man standing wins float maxSecondsBeforeTouchingFloor = 0.0f; int bestAgent = 0; for (int i = 0; i < env.getNumAgents(); ++i) if (agentStates[i].secondsBeforeTouchedFloor > maxSecondsBeforeTouchingFloor) bestAgent = i, maxSecondsBeforeTouchingFloor = agentStates[i].secondsBeforeTouchedFloor; return agentIdx == bestAgent; // winner gets 1, other agents get 0 } else { // fraction of the episode for which we managed to avoid the floor return agentStates[agentIdx].secondsBeforeTouchedFloor / episodeLengthSec(); } } void initializeDefaultParameters() override { auto &fp = floatParams; fp[Str::episodeLengthSec] = 300.0f; fp[Str::verticalLookLimitRad] = 0.75f; } RewardShaping defaultRewardShaping() const override { return { {Str::boxagoneTouchedFloor, -0.1f}, {Str::boxagonePerStepReward, 0.01f}, }; } /** * Each agent is for himself. */ int teamAffinity(int agentIdx) const override { return agentIdx; } private: constexpr static float voxelSize = 2.0f; constexpr static int platformSize = 24; bool finished = false; VoxelGridComponent vg; PlatformsComponent platformsComponent; FallDetectionComponent fallDetection; std::unique_ptr platform; std::vector> disappearingPlatforms; std::vector spawnPositions; std::vector agentStates; std::map platformStates; std::deque extraPlatforms; }; } ================================================ FILE: src/libs/scenarios/include/scenarios/scenario_collect.hpp ================================================ #pragma once #include #include #include #include #include namespace Megaverse { struct VoxelCollect : public VoxelWithPhysicsObjects { Object3D *rewardObject = nullptr; int reward = 0; }; class CollectScenario : public DefaultScenario, public ObjectStackingCallbacks, public FallDetectionCallbacks { public: explicit CollectScenario(const std::string &name, Env &env, Env::EnvState &envState); ~CollectScenario() override; // Scenario interface void reset() override; void createLandscape(); void step() override; void agentFell(int agentIdx) override; std::vector agentStartingPositions() override; void addEpisodeDrawables(DrawablesMap &drawables) override; float trueObjective(int /*agentIdx*/) const override { return solved; } RewardShaping defaultRewardShaping() const override { return { {Str::collectSingleGood, 1.0f}, {Str::collectSingleBad, -1.0f}, {Str::collectAll, 5.0f}, {Str::collectAbyss, -0.5f}, }; } float episodeLengthSec() const override { // add a little bit of time for every extra reward object return Scenario::episodeLengthSec() + 2.0f * rewardPositions.size(); } private: bool solved = false; VoxelGridComponent vg; ObjectStackingComponent objectStackingComponent; FallDetectionComponent fallDetection; std::vector objectPositions, rewardPositions; std::vector agentPositions; int numPositiveRewards = 0, positiveRewardsCollected = 0; }; } ================================================ FILE: src/libs/scenarios/include/scenarios/scenario_default.hpp ================================================ #pragma once #include #include namespace Megaverse { /** * Default base class for scenarios containing some boilerplate code. * Allows us to avoid code duplication for the most standard things. * Scenarios don't have to inherit from DefaultScenario, they are free to inherit directly from scenario to retain * full flexibility. */ class DefaultScenario : public Scenario { public: struct UIElement { public: UIElement() = default; UIElement(Object3D *anchor, Object3D *uiElement) : anchor{anchor} , uiElement{uiElement} { } void rescale(const Magnum::Vector3 &requiredScale) const { const auto scale = uiElement->transformation().scaling(); uiElement->scaleLocal(requiredScale / scale); } void show() { if (visible) return; anchor->translate({-1000, 0, 0}); visible = true; } void hide() { if (!visible) return; anchor->translate({1000, 0, 0}); visible = false; } public: bool visible = true; Object3D *anchor = nullptr; Object3D *uiElement = nullptr; }; struct DefaultUI { std::vector remainingTimeBars, positiveRewardIndicator, negativeRewardIndicator; float initialRemainingTimeBarScale = 0.24f; }; public: explicit DefaultScenario(const std::string &scenarioName, Env &env, Env::EnvState &envState) : Scenario{scenarioName, env, envState} { const auto numAgents = env.getNumAgents(); defaultUI.remainingTimeBars.resize(numAgents); defaultUI.positiveRewardIndicator.resize(numAgents), defaultUI.negativeRewardIndicator.resize(numAgents); } /** * Override this if your scenario needs agents of a different type or a different logic for spawning agents. * @param agents resulting vector of agents */ void spawnAgents(std::vector &agents) override { const auto numAgents = env.getNumAgents(); const auto verticalLookLimitRad = floatParams[Str::verticalLookLimitRad]; const auto agentPositions = agentStartingPositions(); for (int i = 0; i < numAgents; ++i) { auto randomRotation = frand(envState.rng) * Magnum::Constants::pi() * 2; auto &agent = envState.scene->addChild( envState.scene.get(), envState.physics->bWorld, Magnum::Vector3{agentPositions[i]} + Magnum::Vector3{0.5, 0.0, 0.5}, randomRotation, verticalLookLimitRad ); agent.updateTransform(); agents.emplace_back(&agent); } } void addEpisodeAgentsDrawables(DrawablesMap &drawables) override { const auto numAgents = env.getNumAgents(); // drawing agent's body in single-agent envs is still useful because of the overview camera #ifdef DONT_DRAW_SELF if (numAgents <= 1) { // agent can't see itself, so we can avoid wasting resources on rendering it return; } #endif for (int i = 0; i < numAgents; ++i) { auto agent = envState.agents[i]; auto &agentBody = agent->addChild(); agentBody.scale({0.35f, 0.36f, 0.35f}).translate({0, 0.09f, 0}); drawables[DrawableType::Capsule].emplace_back(&agentBody, rgb(agentColors[i % numAgentColors])); auto &eyesObject = agent->getCameraObject()->addChild(); eyesObject.scale({0.25f, 0.12f, 0.2f}).translate({0.0f, 0.0f, -0.19f}); drawables[DrawableType::Box].emplace_back(&eyesObject, rgb(ColorRgb::AGENT_EYES)); // visualize location where agent interacts with objects // auto &pickupSpot = agent.pickupSpot->addChild(); // pickupSpot.scale({0.03f, 0.03f, 0.03f}); // addStandardDrawable(DrawableType::Box, pickupSpot, 0x222222_rgbf); } } void addUIDrawables(DrawablesMap &drawables) override { const auto numAgents = env.getNumAgents(); for (int i = 0; i < numAgents; ++i) { auto agent = envState.agents[i]; auto &uiObject = agent->getCameraObject()->addChild(); uiObject.translate({0, 0, -0.2f}); auto &remainingTimeBarAnchor = uiObject.addChild(); remainingTimeBarAnchor.translate({0, -0.131f, 0}); auto &remainingTimeBar = remainingTimeBarAnchor.addChild(); remainingTimeBar.scaleLocal({defaultUI.initialRemainingTimeBarScale, 0.0015, 0.001}); drawables[DrawableType::Box].emplace_back(&remainingTimeBar, rgb(ColorRgb::BLUE)); defaultUI.remainingTimeBars[i] = UIElement{&remainingTimeBarAnchor, &remainingTimeBar}; if (floatParams[Str::useUIRewardIndicators] > 0) { auto addRewardIndicator = [&](float xOffset, ColorRgb color, std::vector &container) { auto &indicatorAnchor = uiObject.addChild(); indicatorAnchor.translate({xOffset, 0, 0}); auto &indicator = indicatorAnchor.addChild(); indicator.scaleLocal({0.01, 0.03, 0.0001}); drawables[DrawableType::Box].emplace_back(&indicator, rgb(color)); container[i] = UIElement{&indicatorAnchor, &indicator}; container[i].hide(); }; addRewardIndicator(-0.23f, ColorRgb::GREEN, defaultUI.positiveRewardIndicator); addRewardIndicator(0.23f, ColorRgb::RED, defaultUI.negativeRewardIndicator); } } } void updateUI() override { const auto numAgents = env.getNumAgents(); for (int i = 0; i < numAgents; ++i) { auto &bar = defaultUI.remainingTimeBars[i]; bar.rescale({env.remainingTimeFraction() * defaultUI.initialRemainingTimeBarScale, 0.0015, 0.001}); if (floatParams[Str::useUIRewardIndicators] > 0) { if (envState.lastReward[i] > FLT_EPSILON) { defaultUI.positiveRewardIndicator[i].show(); defaultUI.positiveRewardIndicator[i].rescale({0.06, 0.04f * envState.lastReward[i], 0.0001}); defaultUI.negativeRewardIndicator[i].hide(); } else if (envState.lastReward[i] < -FLT_EPSILON) { defaultUI.negativeRewardIndicator[i].show(); defaultUI.negativeRewardIndicator[i].rescale({0.06, -0.04f * envState.lastReward[i], 0.0001}); defaultUI.positiveRewardIndicator[i].hide(); } else { defaultUI.positiveRewardIndicator[i].hide(); defaultUI.negativeRewardIndicator[i].hide(); } } } } std::vector getPalette() const override { std::vector palette; for (auto c : allColors) palette.emplace_back(rgb(c)); return palette; } protected: DefaultUI defaultUI; }; } ================================================ FILE: src/libs/scenarios/include/scenarios/scenario_empty.hpp ================================================ #pragma once #include namespace Megaverse { class EmptyScenario : public DefaultScenario { public: explicit EmptyScenario(const std::string &name, Env &env, Env::EnvState &envState); ~EmptyScenario() override; // Scenario interface void reset() override; void step() override {} std::vector agentStartingPositions() override; void addEpisodeDrawables(DrawablesMap &drawables) override; float trueObjective(int) const override { return 0; } RewardShaping defaultRewardShaping() const override { return {}; } }; } ================================================ FILE: src/libs/scenarios/include/scenarios/scenario_football.hpp ================================================ #pragma once #include #include #include #include namespace Megaverse { class FootballScenario : public DefaultScenario { private: class FootballLayout; public: explicit FootballScenario(const std::string &name, Env &env, Env::EnvState &envState); ~FootballScenario() override; // Scenario interface void reset() override; void step() override; std::vector agentStartingPositions() override; void addEpisodeDrawables(DrawablesMap &drawables) override; float trueObjective(int) const override { return 0; }//TODO RewardShaping defaultRewardShaping() const override { return {}; } private: VoxelGridComponent vg; PlatformsComponent platformsComponent; std::unique_ptr collisionShape; Object3D *footballObject = nullptr; std::unique_ptr layout; }; } ================================================ FILE: src/libs/scenarios/include/scenarios/scenario_hex_explore.hpp ================================================ #pragma once #include #include namespace Megaverse { class HexExploreScenario : public DefaultScenario { public: explicit HexExploreScenario(const std::string &name, Env &env, Env::EnvState &envState); ~HexExploreScenario() override; // Scenario interface void reset() override; void step() override; std::vector agentStartingPositions() override; void addEpisodeDrawables(DrawablesMap &drawables) override; [[nodiscard]] float trueObjective(int) const override { return solved; } RewardShaping defaultRewardShaping() const override { return {{Str::exploreSolved, 5.0f}}; } private: bool solved = false; HexagonalMazeComponent maze; Magnum::Vector3 rewardObjectCoords; Object3D *rewardObject = nullptr; }; } ================================================ FILE: src/libs/scenarios/include/scenarios/scenario_hex_memory.hpp ================================================ #pragma once #include #include #include namespace Megaverse { struct CollectableObject { Object3D *object = nullptr; bool good = false; }; struct VoxelHexMemory { std::list objects; }; class HexMemoryScenario : public DefaultScenario { public: explicit HexMemoryScenario(const std::string &name, Env &env, Env::EnvState &envState); ~HexMemoryScenario() override; // Scenario interface void reset() override; void step() override; std::vector agentStartingPositions() override; void spawnAgents(std::vector &agents) override; void addEpisodeDrawables(DrawablesMap &drawables) override; [[nodiscard]] float trueObjective(int /*agentIdx*/) const override { return solved; } RewardShaping defaultRewardShaping() const override { return { {Str::memoryCollectGood, 1.0f}, {Str::memoryCollectBad, -1.0f}, }; } float episodeLengthSec() const override { // add a little bit of time for every extra reward object return Scenario::episodeLengthSec() + 3.0f * goodObjects.size(); } private: bool solved = false; HexagonalMazeComponent maze; VoxelGridComponent vg; Magnum::Vector3 landmarkLocation; std::vector goodObjects, badObjects; int goodObjectsCollected = 0; }; } ================================================ FILE: src/libs/scenarios/include/scenarios/scenario_obstacles.hpp ================================================ #pragma once #include #include #include #include #include #include #include namespace Megaverse { enum class PlatformType { EMPTY, WALL, LAVA, STEP, GAP }; struct VoxelObstacles : public VoxelWithPhysicsObjects { Object3D *rewardObject = nullptr; }; class ObstaclesScenario : public DefaultScenario, public ObjectStackingCallbacks, public FallDetectionCallbacks { public: explicit ObstaclesScenario(const std::string &name, Env &env, Env::EnvState &envState); // Scenario interface void reset() override; void step() override; std::vector agentStartingPositions() override { return agentSpawnPositions; } void addEpisodeDrawables(DrawablesMap &drawables) override; float trueObjective(int) const override { return solved; } RewardShaping defaultRewardShaping() const override { return { {Str::obstaclesAgentAtExit, 1.0f}, {Str::obstaclesAllAgentsAtExit, 5.0f}, {Str::obstaclesExtraReward, 0.5f}, {Str::obstaclesAgentCarriedObjectToExit, 0.0f}, }; } void initializeDefaultParameters() override { DefaultScenario::initializeDefaultParameters(); platformTypes = {PlatformType::WALL, PlatformType::LAVA, PlatformType::STEP, PlatformType::GAP}; auto &fp = floatParams; fp[Str::obstaclesMinNumPlatforms] = 1; fp[Str::obstaclesMaxNumPlatforms] = 2; fp[Str::obstaclesMinGap] = 1; fp[Str::obstaclesMaxGap] = 2; fp[Str::obstaclesMinLava] = 1; fp[Str::obstaclesMaxLava] = 4; fp[Str::obstaclesMinHeight] = 1; fp[Str::obstaclesMaxHeight] = 3; fp[Str::obstaclesNumAllowedMaxDifficulty] = 1; } float episodeLengthSec() const override; void agentTouchedLava(int agentIdx); void agentFell(int agentIdx) override; protected: std::vector platformTypes; private: VoxelGridComponent vg; PlatformsComponent platformsComponent; ObjectStackingComponent objectStackingComponent; FallDetectionComponent fallDetection; std::vector objectSpawnPositions, rewardSpawnPositions; std::vector agentSpawnPositions; std::vector agentReachedExit; bool solved = false; int numPlatforms{}; }; class TestScenario : public ObstaclesScenario { public: explicit TestScenario(const std::string &name, Env &env, Env::EnvState &envState) : ObstaclesScenario(name, env, envState) { } void initializeDefaultParameters() override { ObstaclesScenario::initializeDefaultParameters(); auto &fp = floatParams; fp[Str::obstaclesMinNumPlatforms] = 0; fp[Str::obstaclesMaxNumPlatforms] = 0; fp[Str::episodeLengthSec] = 6.0f; } }; class ObstaclesEasyScenario : public ObstaclesScenario { public: explicit ObstaclesEasyScenario(const std::string &name, Env &env, Env::EnvState &envState) : ObstaclesScenario(name, env, envState) { } void initializeDefaultParameters() override { ObstaclesScenario::initializeDefaultParameters(); auto &fp = floatParams; fp[Str::obstaclesMinNumPlatforms] = 1; fp[Str::obstaclesMaxNumPlatforms] = 2; fp[Str::obstaclesMinGap] = 1; fp[Str::obstaclesMaxGap] = 2; fp[Str::obstaclesMinLava] = 1; fp[Str::obstaclesMaxLava] = 4; fp[Str::obstaclesMinHeight] = 1; fp[Str::obstaclesMaxHeight] = 3; } }; class ObstaclesMediumScenario : public ObstaclesScenario { public: explicit ObstaclesMediumScenario(const std::string &name, Env &env, Env::EnvState &envState) : ObstaclesScenario(name, env, envState) { } void initializeDefaultParameters() override { ObstaclesScenario::initializeDefaultParameters(); auto &fp = floatParams; fp[Str::obstaclesMinNumPlatforms] = 2; fp[Str::obstaclesMaxNumPlatforms] = 4; fp[Str::obstaclesMinLava] = 2; fp[Str::obstaclesMaxLava] = 5; fp[Str::obstaclesMinHeight] = 1; fp[Str::obstaclesMaxHeight] = 3; } }; class ObstaclesHardScenario : public ObstaclesScenario { public: explicit ObstaclesHardScenario(const std::string &name, Env &env, Env::EnvState &envState) : ObstaclesScenario(name, env, envState) { } void initializeDefaultParameters() override { ObstaclesScenario::initializeDefaultParameters(); auto &fp = floatParams; fp[Str::obstaclesMinNumPlatforms] = 2; fp[Str::obstaclesMaxNumPlatforms] = 7; fp[Str::obstaclesMinGap] = 2; fp[Str::obstaclesMaxGap] = 3; fp[Str::obstaclesMinLava] = 3; fp[Str::obstaclesMaxLava] = 10; fp[Str::obstaclesMinHeight] = 2; fp[Str::obstaclesMaxHeight] = 4; } }; class ObstaclesOnePlatformTypeScenario : public ObstaclesScenario { public: explicit ObstaclesOnePlatformTypeScenario(const std::string &name, Env &env, Env::EnvState &envState) : ObstaclesScenario(name, env, envState) { } RewardShaping defaultRewardShaping() const override { auto shaping = ObstaclesScenario::defaultRewardShaping(); shaping[Str::obstaclesAgentCarriedObjectToExit] = 1.0f; return shaping; } void initializeDefaultParameters() override { ObstaclesScenario::initializeDefaultParameters(); auto &fp = floatParams; fp[Str::obstaclesMinNumPlatforms] = 1; fp[Str::obstaclesMaxNumPlatforms] = 4; fp[Str::obstaclesMinGap] = 1; fp[Str::obstaclesMaxGap] = 3; fp[Str::obstaclesMinLava] = 2; fp[Str::obstaclesMaxLava] = 10; fp[Str::obstaclesMinHeight] = 1; fp[Str::obstaclesMaxHeight] = 3; fp[Str::obstaclesNumAllowedMaxDifficulty] = 1; } }; class ObstaclesOnlyWallsScenario : public ObstaclesOnePlatformTypeScenario { public: explicit ObstaclesOnlyWallsScenario(const std::string &name, Env &env, Env::EnvState &envState) : ObstaclesOnePlatformTypeScenario(name, env, envState) { } void initializeDefaultParameters() override { ObstaclesOnePlatformTypeScenario::initializeDefaultParameters(); platformTypes = {PlatformType::WALL}; } }; class ObstaclesOnlyStepsScenario : public ObstaclesOnePlatformTypeScenario { public: explicit ObstaclesOnlyStepsScenario(const std::string &name, Env &env, Env::EnvState &envState) : ObstaclesOnePlatformTypeScenario(name, env, envState) { } void initializeDefaultParameters() override { ObstaclesOnePlatformTypeScenario::initializeDefaultParameters(); platformTypes = {PlatformType::STEP}; } }; class ObstaclesOnlyLavaScenario : public ObstaclesOnePlatformTypeScenario { public: explicit ObstaclesOnlyLavaScenario(const std::string &name, Env &env, Env::EnvState &envState) : ObstaclesOnePlatformTypeScenario(name, env, envState) { } void initializeDefaultParameters() override { ObstaclesOnePlatformTypeScenario::initializeDefaultParameters(); platformTypes = {PlatformType::LAVA}; } }; } ================================================ FILE: src/libs/scenarios/include/scenarios/scenario_rearrange.hpp ================================================ #pragma once #include #include #include #include #include namespace Megaverse { struct VoxelRearrange : public VoxelState { RigidBody *physicsObject = nullptr; }; struct ArrangementItem { ArrangementItem() = default; ArrangementItem(DrawableType shape, ColorRgb color, const VoxelCoords &offset) : shape{shape} , color{color} , offset{offset} { } static ArrangementItem random(Rng &rng, const VoxelCoords &offset) { const static std::vector shapes{ DrawableType::Cylinder, DrawableType::Capsule, DrawableType::Box, DrawableType::Sphere, }; const auto shape = randomSample(shapes, rng); const auto color = randomObjectColor(rng); ArrangementItem item{shape, color, offset}; return item; } public: DrawableType shape = DrawableType::Sphere; ColorRgb color = ColorRgb::WHITE; VoxelCoords offset{}; }; class ArrangementObject : public RigidBody { public: ArrangementObject(Object3D *parent, Magnum::Float mass, btCollisionShape *bShape, btDynamicsWorld &bWorld) : RigidBody{parent, mass, bShape, bWorld} { } public: ArrangementItem arrangementItem; bool pickedUp = false; }; struct Arrangement { std::vector items; bool contains(DrawableType shape, ColorRgb color, const VoxelCoords &offset) const { for (const auto &item : items) if (item.shape == shape && item.color == color && item.offset == offset) return true; return false; } }; class RearrangeScenario : public DefaultScenario, public ObjectStackingCallbacks { private: class RearrangePlatform; public: explicit RearrangeScenario(const std::string &name, Env &env, Env::EnvState &envState); ~RearrangeScenario() override; // Scenario interface void reset() override; void step() override; std::vector agentStartingPositions() override; void addEpisodeDrawables(DrawablesMap &drawables) override; float trueObjective(int /*agentIdx*/) const override { return solved; } RewardShaping defaultRewardShaping() const override { return { {Str::rearrangeOneMoreObjectCorrectPosition, 1}, {Str::rearrangeAllObjectsCorrectPosition, 10}, }; } void generateArrangement(); void arrangementDrawables(DrawablesMap &drawables, const Arrangement &arr, VoxelCoords center, bool interactive); int countMatchingObjects() const; bool canPlaceObject(int /*agentIdx*/, const VoxelCoords &coords, Object3D *obj) override; void placedObject(int agentIdx, const VoxelCoords &coords, Object3D *obj) override; void pickedObject(int agentIdx, const VoxelCoords &coords, Object3D *obj) override; void checkDone(int agentIdx); private: PlatformsComponent platformsComponent; VoxelGridComponent vg; ObjectStackingComponent objectStackingComponent; std::unique_ptr platform; Arrangement arrangement; std::vector arrangementObjects; int maxMatchingObjects = 0; const VoxelCoords leftCenter = {5, 2, 5}; const VoxelCoords rightCenter = {13, 2, 5}; bool solved = false; }; } ================================================ FILE: src/libs/scenarios/include/scenarios/scenario_sokoban.hpp ================================================ #pragma once #include #include #include #include #include namespace Megaverse { struct SokobanLevel { std::vector rows; }; class SokobanScenario : public DefaultScenario { public: explicit SokobanScenario(const std::string &name, Env &env, Env::EnvState &envState); ~SokobanScenario() override; // Scenario interface void reset() override; void step() override; void reloadLevels(); void createLayout(); std::vector agentStartingPositions() override; void addEpisodeDrawables(DrawablesMap &drawables) override; float trueObjective(int) const override { return float(solved); } RewardShaping defaultRewardShaping() const override { return { {Str::sokobanBoxOnTarget, 1.0f}, {Str::sokobanBoxLeavesTarget, -1.0f}, {Str::sokobanAllBoxesOnTarget, 10.0f}, }; } void initializeDefaultParameters() override { DefaultScenario::initializeDefaultParameters(); floatParams[Str::episodeLengthSec] = 80.0f; } private: std::string boxobanLevelsDir{}; std::vector allSokobanLevelFiles{}; constexpr static ConstStr levelSet = "unfiltered", levelSplit = "train"; std::vector levels; SokobanLevel currLevel; int length = 0, width = 0; VoxelGridComponent vg; const float voxelSize = 2; std::vector agentPositions; std::vector boxesCoords; int numBoxes = 0, numBoxesOnGoal = 0; bool solved = false; }; } ================================================ FILE: src/libs/scenarios/include/scenarios/scenario_tower_building.hpp ================================================ #pragma once #include #include #include #include #include #include #include namespace Megaverse { class TowerBuildingScenario : public DefaultScenario, public ObjectStackingCallbacks, public FallDetectionCallbacks { private: class TowerBuildingPlatform; public: struct AgentState { bool pickedUpObject = false; bool visitedBuildingZoneWithObject = false; }; public: explicit TowerBuildingScenario(const std::string &name, Env &env, Env::EnvState &envState); ~TowerBuildingScenario() override; // Scenario interface void reset() override; void step() override; std::vector agentStartingPositions() override; void addEpisodeDrawables(DrawablesMap &drawables) override; float trueObjective(int) const override { return float(highestTower); } RewardShaping defaultRewardShaping() const override { return { {Str::teamSpirit, 0.1f}, // replaces the default value {Str::towerPickedUpObject, 0.1f}, {Str::towerVisitedBuildingZoneWithObject, 0.1f}, {Str::towerBuildingReward, 1.0f}, }; } float episodeLengthSec() const override; // Callbacks bool canPlaceObject(int agentIdx, const VoxelCoords &voxel, Object3D *obj) override; void placedObject(int agentIdx, const VoxelCoords &voxel, Object3D *obj) override; void pickedObject(int agentIdx, const VoxelCoords &voxel, Object3D *obj) override; private: bool isInBuildingZone(const VoxelCoords &c) const; float calculateTowerReward() const; static float buildingRewardCoeffForHeight(float height); void addCollectiveReward(int agentIdx); private: VoxelGridComponent vg; ObjectStackingComponent objectStackingComponent; FallDetectionComponent fallDetection; PlatformsComponent platformsComponent; int highestTower = 0; BoundingBox buildingZone; std::unordered_set objectsInBuildingZone; float currBuildingZoneReward = 0.0f; std::vector agentState; // previous reward associated with an object (so we can subtract it when we move the object elsewhere) std::map previousReward; std::unique_ptr platform; }; } ================================================ FILE: src/libs/scenarios/src/component_hexagonal_maze.cpp ================================================ #include #include #include #include using namespace Magnum; using namespace Megaverse; HexagonalMazeComponent::HexagonalMazeComponent(Megaverse::Scenario &scenario) : ScenarioComponent{scenario} { } HexagonalMazeComponent::~HexagonalMazeComponent() = default; void HexagonalMazeComponent::reset(Env &, Env::EnvState &envState) { mazeSize = randRange(minSize, maxSize, envState.rng); maze = std::make_unique(mazeSize); Kruskal algorithm; TLOG(DEBUG) << "Initialising graph..."; maze->InitialiseGraph(); TLOG(DEBUG) << "Generating maze..."; maze->GenerateMaze(&algorithm); const auto [xmin, ymin, xmax, ymax] = maze->GetCoordinateBounds(); xMin = xmin, yMin = ymin, xMax = xmax, yMax = ymax; mazeScale = 3.5f; wallHeight = frand(envState.rng) * 0.55f + 0.85f; omitWallsProbability = frand(envState.rng) * (omitWallsProbabilityMax - omitWallsProbabilityMin) + omitWallsProbabilityMin; wallLandmarkProbability = frand(envState.rng) * 0.15f + 0.15f; bottomEdgingColor = sampleRandomColor(envState.rng); topEdgingColor = sampleRandomColor(envState.rng); xMin *= mazeScale, xMax *= mazeScale, yMin *= mazeScale, yMax *= mazeScale; } void HexagonalMazeComponent::addDrawablesAndCollisions(DrawablesMap &drawables, Env::EnvState &envState) const { auto scale = Magnum::Vector3{float(xMax - xMin), 0.0001f, float(yMax - yMin)}; auto translation = Magnum::Vector3{float(xMax + xMin) / 2, 0.0f, float(yMax + yMin) / 2}; addStaticCollidingBox(drawables, envState, scale, translation, randomLayoutColor(envState.rng)); std::set> existingWalls; auto adjList = maze->getAdjacencyList(); for (auto cellIdx = 0; cellIdx < int(adjList.size()); ++cellIdx) { auto &cellAdjList = adjList[cellIdx]; for (auto &[adjCellIdx, border] : cellAdjList) { auto cellPair = std::make_pair(cellIdx, adjCellIdx); if (cellPair.first > cellPair.second) std::swap(cellPair.first, cellPair.second); // sort the pair // check if this is not an outside border if (adjCellIdx != -1) { if (existingWalls.count(cellPair)) continue; if (frand(envState.rng) < omitWallsProbability) { // remove this particular wall continue; } } existingWalls.insert(cellPair); const auto lineBorder = dynamic_cast(border.get()); auto [x1, z1, x2, z2] = lineBorder->getBorderCoords(); x1 *= mazeScale, z1 *= mazeScale, x2 *= mazeScale, z2 *= mazeScale; const auto length = 0.5f * sqrtf(float(sqr(x1 - x2) + sqr(z1 - z2))); const Vector3 wallScale{length, wallHeight, 0.15f}; const Vector3 wallTranslation{float(x1 + x2) / 2, wallHeight, float(z1 + z2) / 2}; const auto deltaX = x1 - x2; const auto deltaZ = z1 - z2; float rotationY = M_PI_2; if (std::fabs(deltaX) > EPSILON) { const auto tanAlpha = deltaZ / deltaX; rotationY = -atanf(tanAlpha); } auto &layoutBox = envState.scene->addChild(); if (frand(envState.rng) < wallLandmarkProbability) { const auto landmarkWidth = 0.15f, landmarkHeight = landmarkWidth * length / wallHeight; int numLandmarks = randRange(2, 5, envState.rng); for (int li = 0; li < numLandmarks; ++li) { const Vector3 landmarkScale{landmarkWidth, landmarkHeight, frand(envState.rng) * 1.2f + 1.5f}; auto &landmarkBox = layoutBox.addChild(); const Vector3 landmarkTranslation{float(li % 2 == 1) * landmarkWidth * 2, float(li > 1) * landmarkHeight * 2 - 0.2f, 0}; landmarkBox.scaleLocal(landmarkScale).translate(landmarkTranslation); drawables[DrawableType::Box].emplace_back(&landmarkBox, rgb(sampleRandomColor(envState.rng))); } } layoutBox.scale(wallScale).rotateY(Rad(rotationY)).translate(wallTranslation); drawables[DrawableType::Box].emplace_back(&layoutBox, rgb(ColorRgb::DARK_BLUE)); auto bBoxShape = std::make_unique(btVector3{1, 1, 1}); auto &collisionBox = layoutBox.addChild(envState.scene.get(), 0.0f, bBoxShape.get(), envState.physics->bWorld); collisionBox.syncPose(); envState.physics->collisionShapes.emplace_back(std::move(bBoxShape)); // top and bottom edging { auto &bottomEdgingBox = envState.scene->addChild(); const Vector3 edgingScale{length * 1.02f, wallHeight * 0.12f, 0.2f}; const Vector3 bottomEdgingTranslation{wallTranslation.x(), edgingScale.y(), wallTranslation.z()}; bottomEdgingBox.scale(edgingScale).rotateY(Rad(rotationY)).translate(bottomEdgingTranslation); drawables[DrawableType::Box].emplace_back(&bottomEdgingBox, rgb(bottomEdgingColor)); // auto &topEdgingBox = envState.scene->addChild(); // const Vector3 topEdgingTranslation{wallTranslation.x(), wallHeight * 2, wallTranslation.z()}; // topEdgingBox.scale(edgingScale).rotateY(Rad(rotationY)).translate(topEdgingTranslation); // drawables[DrawableType::Box].emplace_back(&topEdgingBox, rgb(topEdgingColor)); } } } } ================================================ FILE: src/libs/scenarios/src/layout_utils.cpp ================================================ #include #include #include #include #include using namespace Magnum; using namespace Magnum::Math::Literals; using namespace Megaverse; // TODO: add different types of layouts void Megaverse::addBoundingBoxes(DrawablesMap &drawables, Env::EnvState &envState, const Boxes &boxes, int voxelType, ColorRgb color, float voxelSize) { if (voxelType == VOXEL_EMPTY) return; for (auto box : boxes) { const auto bboxMin = box.min, bboxMax = box.max; auto scale = Magnum::Vector3{ float(bboxMax.x() - bboxMin.x() + 1) / 2, float(bboxMax.y() - bboxMin.y() + 1) / 2, float(bboxMax.z() - bboxMin.z() + 1) / 2, } * voxelSize; auto translation = Magnum::Vector3{ float((bboxMin.x() + bboxMax.x())) / 2 + 0.5f, float((bboxMin.y() + bboxMax.y())) / 2 + 0.5f, float((bboxMin.z() + bboxMax.z())) / 2 + 0.5f } * voxelSize; auto &layoutBox = envState.scene->addChild(); layoutBox.scale(scale).translate(translation); if (voxelType & VOXEL_OPAQUE) drawables[DrawableType::Box].emplace_back(&layoutBox, rgb(color)); if (voxelType & VOXEL_SOLID) { auto bBoxShape = std::make_unique(btVector3{1,1,1}); auto &collisionBox = layoutBox.addChild(envState.scene.get(), 0.0f, bBoxShape.get(), envState.physics->bWorld); collisionBox.syncPose(); envState.physics->collisionShapes.emplace_back(std::move(bBoxShape)); } } } void Megaverse::addTerrain(DrawablesMap &drawables, Env::EnvState &envState, TerrainType type, const BoundingBox &bb, float voxelSize) { const auto scale = Vector3(bb.max.x() - bb.min.x(), 1.0, bb.max.z() - bb.min.z()) * voxelSize; if (scale.x() > 0) { // otherwise we don't draw anything const auto pos = Magnum::Vector3(bb.min.x() * voxelSize + scale.x() / 2, bb.min.y() * voxelSize, bb.min.z() * voxelSize + scale.z() / 2); auto &terrainObject = envState.scene->addChild(envState.scene.get()); terrainObject.scale({0.5, 0.025, 0.5}).scale(scale); terrainObject.translate({0.0, 0.025, 0.0}); terrainObject.translate(pos); drawables[DrawableType::Box].emplace_back(&terrainObject, rgb(terrainColor(type))); } } void Megaverse::addStaticCollidingBox( DrawablesMap &drawables, Env::EnvState &envState, Vector3 scale, Vector3 translation, ColorRgb color ) { auto &layoutBox = envState.scene->addChild(); layoutBox.scale(scale).translate(translation); drawables[DrawableType::Box].emplace_back(&layoutBox, rgb(color)); auto bBoxShape = std::make_unique(btVector3{1, 1, 1}); auto &collisionBox = layoutBox.addChild(envState.scene.get(), 0.0f, bBoxShape.get(), envState.physics->bWorld); collisionBox.syncPose(); envState.physics->collisionShapes.emplace_back(std::move(bBoxShape)); } Object3D * Megaverse::addCylinder(DrawablesMap &drawables, Object3D &parent, Magnum::Vector3 translation, Magnum::Vector3 scale, ColorRgb color) { auto &rootObject = parent.addChild(); rootObject.scale(scale).translate(translation); drawables[DrawableType::Cylinder].emplace_back(&rootObject, rgb(color)); return &rootObject; } Object3D * Megaverse::addSphere(DrawablesMap &drawables, Object3D &parent, Magnum::Vector3 translation, Magnum::Vector3 scale, ColorRgb color) { auto &rootObject = parent.addChild(); rootObject.scale(scale).translate(translation); drawables[DrawableType::Sphere].emplace_back(&rootObject, rgb(color)); return &rootObject; } Object3D * Megaverse::addPillar(DrawablesMap &drawables, Object3D &parent, Magnum::Vector3 translation, Magnum::Vector3 scale, ColorRgb color) { auto rootObject = addCylinder(drawables, parent, translation, scale, color); auto capScale = Vector3 {scale.x() * 1.2f, 0.15f, scale.z() * 1.2f}; auto capTranslation = Vector3 {0, 0.47, 0} * scale; addCylinder(drawables, parent, translation + capTranslation, capScale, color)->setParentKeepTransformation(rootObject); addCylinder(drawables, parent, translation - capTranslation, capScale, color)->setParentKeepTransformation(rootObject); return rootObject; } Object3D * Megaverse::addDiamond(DrawablesMap &drawables, Object3D &parent, Magnum::Vector3 translation, Magnum::Vector3 scale, ColorRgb color) { auto &rootObject = parent.addChild(); auto &bottomHalf = rootObject.addChild(); bottomHalf.rotateXLocal(180.0_degf).translate({0.0f, -1.0f, 0.0f}); rootObject.scale(scale); rootObject.translate(translation); drawables[DrawableType::Cone].emplace_back(&rootObject, rgb(color)); drawables[DrawableType::Cone].emplace_back(&bottomHalf, rgb(color)); return &rootObject; } ================================================ FILE: src/libs/scenarios/src/scenario_box_a_gone.cpp ================================================ #include using namespace Megaverse; class BoxAGoneScenario::BoxAGonePlatform : public EmptyPlatform { public: explicit BoxAGonePlatform(Object3D *parent, Rng &rng, int walls, const FloatParams ¶ms, int) : EmptyPlatform(parent, rng, walls, params) { } ~BoxAGonePlatform() = default; void init() override { height = 8; length = width = platformSize; } void generate() override { EmptyPlatform::generate(); } }; BoxAGoneScenario::BoxAGoneScenario(const std::string &name, Env &env, Env::EnvState &envState) : DefaultScenario(name, env, envState) , vg{*this, 100, 0, 0, 0, voxelSize} , platformsComponent{*this} , fallDetection{*this, vg.grid, *this} { } BoxAGoneScenario::~BoxAGoneScenario() = default; void BoxAGoneScenario::reset() { finished = false; vg.reset(env, envState); platformsComponent.reset(env, envState); fallDetection.reset(env, envState); disappearingPlatforms.clear(), spawnPositions.clear(); agentStates = std::vector(env.getNumAgents()); platformStates.clear(); extraPlatforms.clear(); platform = std::make_unique(platformsComponent.levelRoot.get(), envState.rng, WALLS_ALL, floatParams, env.getNumAgents()); platform->init(), platform->generate(); vg.addPlatform(*platform, ColorRgb::LAYOUT_DEFAULT, ColorRgb::LAYOUT_DEFAULT, true); const int numLevels = randRange(2, 4, envState.rng); const static std::vector colors{ColorRgb::ORANGE, ColorRgb::BLUE, ColorRgb::VIOLET}; const static auto numColors = colors.size(); int currLevelHeight = 1; for (int level = 0; level < numLevels; ++level) { const auto color = colors[level % numColors]; currLevelHeight += randRange(2, 4, envState.rng); const auto offset = platformSize / 2; const int levelLength = randRange(10, 19, envState.rng); const int levelWidth = randRange(10, 19, envState.rng); const int startX = offset - levelLength / 2, startZ = offset - levelWidth / 2; const float skipProb = frand(envState.rng) * 0.2f; // uniformly distributed between 0% and 25% for (int x = startX; x < startX + levelLength; ++x) for (int z = startZ; z < startZ + levelWidth; ++z) { // skip some platforms if (frand(envState.rng) < skipProb) continue; VoxelCoords coords{x, currLevelHeight, z}; disappearingPlatforms.emplace_back(coords, color); if (level == numLevels - 1) { Magnum::Vector3 v(float(x) + 0.5f, float(currLevelHeight) + 0.5f, float(z) + 0.5f); spawnPositions.emplace_back(v * voxelSize); } } } while (int(spawnPositions.size()) < env.getNumAgents()) spawnPositions.emplace_back(spawnPositions[0]); std::shuffle(spawnPositions.begin(), spawnPositions.end(), envState.rng); } void BoxAGoneScenario::step() { int agentsTouchingFloor = 0; for (int i = 0; i < env.getNumAgents(); ++i) { auto agent = envState.agents[i]; const auto t = agent->absoluteTransformation().translation(); const auto coords = vg.grid.getCoords(t); const bool touchesFloor = coords.y() < 3; if (touchesFloor) { rewardTeam(Str::boxagoneTouchedFloor, i, 1); // the rewards here are purely individual (each agent belongs to their own team, although this can be changed) ++agentsTouchingFloor; } else { rewardTeam(Str::boxagonePerStepReward, i, 1); agentStates[i].secondsBeforeTouchedFloor = envState.currEpisodeSec; } auto voxel = vg.grid.get(coords); if (voxel && voxel->disappearingPlatform && agent->onGround()) { if (voxel->disappearingPlatform != agentStates[i].lastPlatform) { // visited new platform // set the timer for the previous visited platform to disappear if (platformStates.count(agentStates[i].lastPlatform)) { auto &p = platformStates[agentStates[i].lastPlatform]; p.remainingTicks = std::min(p.remainingTicks, 3); } // add new platform state if (!platformStates.count(voxel->disappearingPlatform)) { const static auto ticks = 15; const auto temporaryPlatform = extraPlatforms.back(); platformStates[voxel->disappearingPlatform] = PlatformState{ticks, coords, temporaryPlatform}; extraPlatforms.pop_back(); extraPlatforms.push_front(temporaryPlatform); const auto platformSc = voxel->disappearingPlatform->absoluteTransformation().scaling(); const auto platformTr = voxel->disappearingPlatform->absoluteTransformation().translation(); temporaryPlatform->resetTransformation(); temporaryPlatform->scale(platformSc * 1.05f); temporaryPlatform->translate(platformTr); temporaryPlatform->syncPose(); voxel->disappearingPlatform->translate(Magnum::Vector3{300, 300, 300} * voxelSize); // basically remove from the scene voxel->disappearingPlatform->syncPose(); } agentStates[i].lastPlatform = voxel->disappearingPlatform; } } } for (auto iter = platformStates.begin(); iter != platformStates.end();) { auto &state = iter->second; auto &tempPlatform = state.temporaryPlatform; --state.remainingTicks; if (state.remainingTicks <= 0) { tempPlatform->translate(Magnum::Vector3{300, 300, 300} * voxelSize); // basically remove from the scene tempPlatform->syncPose(); platformStates.erase(iter++); vg.grid.remove(state.coords); } else { if (state.remainingTicks <= 5) { const auto platformSc = tempPlatform->absoluteTransformation().scaling(); const auto platformTr = tempPlatform->absoluteTransformation().translation(); tempPlatform->resetTransformation().scale(platformSc * 1.03f).translate(platformTr); tempPlatform->syncPose(); } ++iter; } } if (agentsTouchingFloor >= env.getNumAgents() && !finished) { finished = true; doneWithTimer(); } } std::vector BoxAGoneScenario::agentStartingPositions() { return std::vector{spawnPositions.begin(), spawnPositions.begin() + env.getNumAgents()}; } void BoxAGoneScenario::addEpisodeDrawables(DrawablesMap &drawables) { addDrawablesAndCollisionObjectsFromVoxelGrid(vg, drawables, envState, voxelSize); // add terrain for (auto &[terrainType, boxes] : platform->terrainBoxes) for (auto &bb : boxes) addTerrain(drawables, envState, terrainType, bb.boundingBox(), voxelSize); addDisappearingPlatforms(drawables); } void BoxAGoneScenario::addDisappearingPlatforms(DrawablesMap &drawables) { auto objSize = 0.42f * voxelSize; auto thicknessRatio = 0.045f; auto objScale = Magnum::Vector3{objSize, objSize * thicknessRatio, objSize}; for (const auto &[pos, color] : disappearingPlatforms) { auto translation = Magnum::Vector3{float(pos.x()) + 0.5f, float(pos.y()) + 0.5f, float(pos.z()) + 0.5f} * voxelSize; auto bBoxShape = std::make_unique(btVector3{1, 1, 1}); auto &object = envState.scene->addChild(envState.scene.get(), 0.0f, bBoxShape.get(), envState.physics->bWorld); object.scale(objScale).translate(translation); object.syncPose(); drawables[DrawableType::Box].emplace_back(&object, rgb(color)); envState.physics->collisionShapes.emplace_back(std::move(bBoxShape)); VoxelBoxAGone voxelState; voxelState.disappearingPlatform = &object; vg.grid.set(pos, voxelState); } for (int i = 0; i < env.getNumAgents() * 3; ++i) { auto translation = Magnum::Vector3{300, 300, 300} * voxelSize; auto bBoxShape = std::make_unique(btVector3{1, 1, 1}); auto &object = envState.scene->addChild(envState.scene.get(), 0.0f, bBoxShape.get(), envState.physics->bWorld); object.scale(objScale).translate(translation); object.syncPose(); drawables[DrawableType::Box].emplace_back(&object, rgb(ColorRgb::GREEN)); envState.physics->collisionShapes.emplace_back(std::move(bBoxShape)); extraPlatforms.emplace_back(&object); } } ================================================ FILE: src/libs/scenarios/src/scenario_collect.cpp ================================================ #include #include using namespace Megaverse; using namespace Magnum::Math::Literals; CollectScenario::CollectScenario(const std::string &name, Env &env, Env::EnvState &envState) : DefaultScenario(name, env, envState) , vg{*this} , objectStackingComponent{*this, env.getNumAgents(), vg.grid, *this} , fallDetection{*this, vg.grid, *this} { } CollectScenario::~CollectScenario() = default; void CollectScenario::reset() { solved = false; vg.reset(env, envState); objectStackingComponent.reset(env, envState); fallDetection.reset(env, envState); numPositiveRewards = positiveRewardsCollected = 0; createLandscape(); fallDetection.agentInitialPositions = agentPositions; } void CollectScenario::createLandscape() { auto &rng = envState.rng; static const std::vector landscapeColors = { ColorRgb::LAYOUT_DEFAULT, ColorRgb::VERY_LIGHT_GREEN, ColorRgb::VERY_LIGHT_BLUE, ColorRgb::VERY_LIGHT_GREY, ColorRgb::VERY_LIGHT_ORANGE, ColorRgb::GREY, ColorRgb::DARK_GREY, }; static const std::vector floorColors = { ColorRgb::GREY, ColorRgb::DARK_GREY, ColorRgb::DARK_GREY, }; auto landscapeColor = randomSample(landscapeColors, rng); auto floorColor = randomSample(floorColors, rng); constexpr static int maxWidth = 42, maxLength = maxWidth; const int width = randRange(8, maxWidth, rng); const int length = randRange(8, maxWidth, rng); std::vector spawnHeight(length * width, 1); double frequency = double (randRange(1, 100, rng)) / 10.0; // frequency = std::clamp(frequency, 0.1, 64.0); const std::int32_t octaves = randRange(1, 10, rng); // octaves = std::clamp(octaves, 1, 16); const std::uint32_t seed = randRange(0, 1000000000, rng); const siv::PerlinNoise perlin(seed); const double fx = maxLength / frequency; const double fz = maxWidth / frequency; const int intensity = randRange(5, 18, rng); const float groundLevel = Megaverse::frand(rng) * 0.5f + 0.2f; for (int x = 1; x < length - 1; ++x) for (int z = 1; z < width - 1; ++z) { const double noise = perlin.accumulatedOctaveNoise2D_0_1(x / fx, z / fz, octaves); const double yCoord = intensity * (noise - groundLevel); if (yCoord >= 1) { const int yCoordRound = int(lround(yCoord)); for (int y = yCoordRound; y >= 1; --y) { VoxelCoords v{x, y, z}; vg.grid.set(v, makeVoxel(VOXEL_SOLID | VOXEL_OPAQUE, TERRAIN_NONE, landscapeColor)); } spawnHeight[x * width + z] = yCoordRound + 1; } } // floor for (int x = 0; x < length; ++x) for (int z = 0; z < width; ++z) vg.grid.set({x, 0, z}, makeVoxel(VOXEL_SOLID | VOXEL_OPAQUE, TERRAIN_NONE, floorColor)); std::vector spawnPositions; for (int x = 1; x < length - 1; ++x) for (int z = 1; z < width - 1; ++z) { const int y = spawnHeight[x * width + z]; spawnPositions.emplace_back(x, y, z); } std::shuffle(spawnPositions.begin(), spawnPositions.end(), rng); int offset = 0; auto agentIntPositions = std::vector(spawnPositions.begin() + offset, spawnPositions.begin() + offset + env.getNumAgents()); agentPositions = toFloat(agentIntPositions); offset += env.getNumAgents(); int numRewards = randRange(1, int(lround(0.05 * width * length)) + 2, rng); numRewards = std::min(numRewards, int(spawnPositions.size()) - offset); int numRewardsPlacedRandomly = std::max(numRewards / 2, 1); // place half of the reward randomly rewardPositions = std::vector(spawnPositions.begin() + offset, spawnPositions.begin() + offset + numRewardsPlacedRandomly); offset += numRewardsPlacedRandomly; std::sort(spawnPositions.begin() + offset, spawnPositions.end(), [&](const VoxelCoords &a, const VoxelCoords &b) { int heightA = spawnHeight[a.x() * width + a.z()]; int heightB = spawnHeight[b.x() * width + b.z()]; if (heightA != heightB) return heightA > heightB; else return false; // equal }); rewardPositions.insert(rewardPositions.end(), spawnPositions.begin() + offset, spawnPositions.begin() + offset + (numRewards - numRewardsPlacedRandomly)); offset += numRewards - numRewardsPlacedRandomly; std::shuffle(spawnPositions.begin() + offset, spawnPositions.end(), rng); auto objectsMin = std::max(3, int(length * width * 0.04)); auto objectsMax = std::min(objectsMin + 1, int(lround(0.07 * width * length)) + 2); const int numObjects = std::min(randRange(objectsMin, objectsMax, rng), int(spawnPositions.size()) - offset); if (offset + numObjects < int(spawnPositions.size())) { objectPositions = std::vector(spawnPositions.begin() + offset, spawnPositions.begin() + offset + numObjects); offset += numObjects; } } void CollectScenario::step() { objectStackingComponent.step(env, envState); fallDetection.step(env, envState); for (int i = 0; i < env.getNumAgents(); ++i) { const auto agent = envState.agents[i]; auto t = agent->absoluteTransformation().translation(); VoxelCoords voxel = vg.grid.getCoords(t); auto voxelPtr = vg.grid.get(voxel); if (voxelPtr && voxelPtr->rewardObject) { // TLOG(INFO) << "Reward " << voxelPtr->reward; Magnum::Vector3 far = {500, 500, 500}; voxelPtr->rewardObject->translate(far); if (voxelPtr->reward > 0) ++positiveRewardsCollected; if (voxelPtr->reward > 0) rewardTeam(Str::collectSingleGood, i, 1); else if (voxelPtr->reward < 0) rewardTeam(Str::collectSingleBad, i, 1); if (positiveRewardsCollected >= numPositiveRewards && !solved) { TLOG(INFO) << "All rewards collected!"; solved = true; doneWithTimer(); rewardTeam(Str::collectAll, i, 1); } vg.grid.remove(voxel); } } } std::vector CollectScenario::agentStartingPositions() { return agentPositions; } void CollectScenario::addEpisodeDrawables(DrawablesMap &drawables) { addDrawablesAndCollisionObjectsFromVoxelGrid(vg, drawables, envState, 1); objectStackingComponent.addDrawablesAndCollisions(drawables, envState, objectPositions); // adding reward objects for (const auto &pos : rewardPositions) { auto translation = Magnum::Vector3{float(pos.x()) + 0.5f, float(pos.y()) + 0.8f, float(pos.z()) + 0.5f}; auto voxel = makeVoxel(VOXEL_EMPTY); ColorRgb color; if (frand(envState.rng) > 0.3f) { voxel.reward = +1; color = ColorRgb::GREEN; ++numPositiveRewards; } else { voxel.reward = -1; color = ColorRgb::RED; } auto rewardObject = addDiamond(drawables, *envState.scene, translation, {0.17f, 0.45f, 0.17f}, color); voxel.rewardObject = rewardObject; vg.grid.set(pos, voxel); } } void CollectScenario::agentFell(int agentIdx) { // this is just to help agents learn a bit faster rewardAgent(Str::collectSingleBad, agentIdx, 1); } ================================================ FILE: src/libs/scenarios/src/scenario_empty.cpp ================================================ #include #include using namespace Megaverse; EmptyScenario::EmptyScenario(const std::string &name, Env &env, Env::EnvState &envState) : DefaultScenario(name, env, envState) { } EmptyScenario::~EmptyScenario() = default; void EmptyScenario::reset() { } std::vector EmptyScenario::agentStartingPositions() { return std::vector(env.getNumAgents(), {1, 1, 1}); } void EmptyScenario::addEpisodeDrawables(DrawablesMap &drawables) { addStaticCollidingBox(drawables, envState, {10, 1, 10}, {5, 0, 5}, ColorRgb::BLUE); } ================================================ FILE: src/libs/scenarios/src/scenario_football.cpp ================================================ #include using namespace Megaverse; class FootballScenario::FootballLayout : public EmptyPlatform { public: FootballLayout(Object3D *parent, Rng &rng, int walls, const FloatParams ¶ms) : EmptyPlatform{parent, rng, walls, params} { } void init() override { length = randRange(14, 24, rng); if (width == -1) width = randRange(12, 24, rng); height = randRange(3, 7, rng); } }; // TODO: refactor this? class DynamicRigidBody : public Object3D { public: DynamicRigidBody(Object3D *parent, Magnum::Float mass, btCollisionShape *bShape, btDynamicsWorld &bWorld) : Object3D{parent}, bWorld(bWorld) { /* Calculate inertia so the object reacts as it should with rotation and everything */ btVector3 bInertia(0.0f, 0.0f, 0.0f); if (!Magnum::Math::TypeTraits::equals(mass, 0.0f)) bShape->calculateLocalInertia(mass, bInertia); // Bullet rigid body setup motionState = std::make_unique(*this); auto info = btRigidBody::btRigidBodyConstructionInfo{mass, &motionState->btMotionState(), bShape, bInertia}; info.m_friction = 0.5; info.m_rollingFriction = 0.1; info.m_spinningFriction = 0.1; bRigidBody.emplace(info); // bRigidBody->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); bRigidBody->forceActivationState(DISABLE_DEACTIVATION); // do we need this? bWorld.addRigidBody(bRigidBody.get()); // auto mask = bRigidBody->getBroadphaseHandle()->m_collisionFilterMask; colliding = true; } ~DynamicRigidBody() override { if (colliding) bWorld.removeRigidBody(bRigidBody.get()); bRigidBody.reset(); } btRigidBody &rigidBody() { return *bRigidBody; } /** * Allows to set collsion shape scale relative to the object scale */ void setCollisionScale(const Magnum::Vector3 &scale) { collisionScale = scale; } /* needed after changing the pose from Magnum side */ void syncPose() { const auto &m = absoluteTransformationMatrix(); bRigidBody->setWorldTransform(btTransform{btMatrix3x3{m.rotation()}, btVector3{m.translation()}}); bRigidBody->getCollisionShape()->setLocalScaling(btVector3{m.scaling() * collisionScale}); } void toggleCollision() { // bRigidBody->setCollisionFlags(bRigidBody->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT); // bRigidBody->setActivationState(DISABLE_SIMULATION); if (colliding) { bWorld.removeRigidBody(bRigidBody.get()); colliding = false; } else { bWorld.addRigidBody(bRigidBody.get()); colliding = true; } } public: btDynamicsWorld &bWorld; Magnum::Containers::Pointer bRigidBody; std::unique_ptr motionState; Magnum::Vector3 collisionScale{1, 1, 1}; bool colliding = false; }; FootballScenario::FootballScenario(const std::string &name, Env &env, Env::EnvState &envState) : DefaultScenario(name, env, envState) , vg{*this} , platformsComponent{*this} { } FootballScenario::~FootballScenario() = default; void FootballScenario::reset() { vg.reset(env, envState); platformsComponent.reset(env, envState); collisionShape = std::make_unique(2.0); auto &object = envState.scene->addChild(envState.scene.get(), 1.0f, collisionShape.get(), envState.physics->bWorld); auto translation = Magnum::Vector3{5, 5, 5}; object.scale({0.5, 0.5, 0.5}).translate(translation); object.syncPose(); footballObject = &object; layout = std::make_unique(platformsComponent.levelRoot.get(), envState.rng, WALLS_ALL, floatParams); layout->init(), layout->generate(); vg.addPlatform(*layout, ColorRgb::LAYOUT_DEFAULT, ColorRgb::LAYOUT_DEFAULT, true); } std::vector FootballScenario::agentStartingPositions() { return layout->agentSpawnPoints(env.getNumAgents()); } void FootballScenario::addEpisodeDrawables(DrawablesMap &drawables) { addDrawablesAndCollisionObjectsFromVoxelGrid(vg, drawables, envState, 1); drawables[DrawableType::Sphere].emplace_back(footballObject, rgb(ColorRgb::ORANGE)); } void FootballScenario::step() { for (int i = 0; i < env.getNumAgents(); ++i) { const auto a = envState.currAction[i]; if (!!(a & Action::Interact)) { const auto &agent = envState.agents[i]; const auto t = agent->transformation().translation(); const auto ft = footballObject->transformation().translation(); auto dt = ft - t; if (dt.length() < 1.8) { auto dtNorm = dt.normalized(); dtNorm.y() = 0.5; auto force = 70 * btVector3{dtNorm.x(), dtNorm.y(), dtNorm.z()}; auto football = dynamic_cast(footballObject); football->bRigidBody->applyForce(force, {0, 0, 0}); } } } } ================================================ FILE: src/libs/scenarios/src/scenario_hex_explore.cpp ================================================ #include #include #include #include using namespace Megaverse; using namespace Magnum; using namespace Magnum::Math::Literals; HexExploreScenario::HexExploreScenario(const std::string &name, Env &env, Env::EnvState &envState) : DefaultScenario(name, env, envState) , maze{*this} { } HexExploreScenario::~HexExploreScenario() = default; void HexExploreScenario::reset() { solved = false; maze.minSize = 2, maze.maxSize = 8; maze.omitWallsProbabilityMin = 0.1f, maze.omitWallsProbabilityMax = 0.4f; maze.reset(env, envState); auto &hexMaze = maze.getMaze(); auto &adjList = hexMaze.getAdjacencyList(); auto mazeScale = maze.getScale(); auto randomCellIdx = randRange(0, adjList.size(), envState.rng); auto cellCenter = hexMaze.getCellCenters()[randomCellIdx]; rewardObjectCoords = Magnum::Vector3{float(cellCenter.first) * mazeScale, 0, float(cellCenter.second) * mazeScale}; rewardObject = nullptr; } void HexExploreScenario::step() { for (int i = 0; i < env.getNumAgents(); ++i) { auto &agent = env.getAgents()[i]; const auto t = agent->absoluteTransformation().translation(); const auto threshold = 1.2; const auto distance = (t - rewardObjectCoords).length(); if (distance < threshold && !solved) { solved = true; doneWithTimer(); rewardTeam(Str::exploreSolved, i, 1); rewardObject->translate({1e3, 1e3, 1e3}); break; } } } std::vector HexExploreScenario::agentStartingPositions() { std::vector positions; auto &hexMaze = maze.getMaze(); auto mazeScale = maze.getScale(); std::vector cellIndices(hexMaze.getAdjacencyList().size(), 0); std::iota(cellIndices.begin(), cellIndices.end(), 0); std::shuffle(cellIndices.begin(), cellIndices.end(), envState.rng); float furtherstDistance = 0; for (auto cellIdx : cellIndices) { auto cellCenter = hexMaze.getCellCenters()[cellIdx]; auto spawnPos = Vector3{float(cellCenter.first) * mazeScale, 0.1, float(cellCenter.second) * mazeScale}; auto distance = (rewardObjectCoords - spawnPos).length(); auto rotation = float(2 * M_PI / env.getNumAgents()); if (distance > furtherstDistance) { positions.clear(); for (int i = 0; i < env.getNumAgents(); ++i) { auto delta = Vector3{sinf(float(i) * rotation), 0, cosf(float(i) * rotation)}; positions.emplace_back(spawnPos + delta); } furtherstDistance = distance; } if (distance > float(maze.getSize()) * maze.getScale()) break; } if (positions.empty()) { TLOG(DEBUG) << "Could not generate valid spawn positions for the agents"; positions = std::vector(env.getNumAgents(), Vector3{0, 1, 0}); } return positions; } void HexExploreScenario::addEpisodeDrawables(DrawablesMap &drawables) { maze.addDrawablesAndCollisions(drawables, envState); // adding reward object const auto scale = 1.9f; rewardObject = addDiamond(drawables, *envState.scene, rewardObjectCoords + Vector3{0, 1.2, 0}, {0.17f * scale, 0.35f * scale, 0.17f * scale}, ColorRgb::VIOLET); } ================================================ FILE: src/libs/scenarios/src/scenario_hex_memory.cpp ================================================ #include #include #include using namespace Magnum; using namespace Megaverse; HexMemoryScenario::HexMemoryScenario(const std::string &name, Env &env, Env::EnvState &envState) : DefaultScenario(name, env, envState) , maze{*this} , vg{*this, 100, 0, 0, 0, 1.0} { } HexMemoryScenario::~HexMemoryScenario() = default; void HexMemoryScenario::reset() { solved = false; vg.reset(env, envState); goodObjects.clear(), badObjects.clear(); goodObjectsCollected = 0; maze.minSize = 2, maze.maxSize = 8; maze.omitWallsProbabilityMin = 0.1f, maze.omitWallsProbabilityMax = 0.95f; maze.reset(env, envState); auto &hexMaze = maze.getMaze(); auto &adjList = hexMaze.getAdjacencyList(); auto mazeScale = maze.getScale(); float minDistanceToCenter = 1e9f; int centerCellIdx = 0; // hacky way to find the cell closest to center for (int cellIdx = 0; cellIdx < int(adjList.size()); ++cellIdx) { auto cellCenter = hexMaze.getCellCenters()[cellIdx]; auto distanceToCenter = sqrt(sqr(cellCenter.first) + sqr(cellCenter.second)); if (distanceToCenter < minDistanceToCenter) { centerCellIdx = cellIdx; minDistanceToCenter = distanceToCenter; } } auto mazeCenter = hexMaze.getCellCenters()[centerCellIdx]; landmarkLocation = Magnum::Vector3(mazeCenter.first * mazeScale, 1.0f, mazeCenter.second * mazeScale); std::vector objectCoordinates; for (int cellIdx = 0; cellIdx < int(adjList.size()); ++cellIdx) { if (cellIdx == centerCellIdx) continue; auto cellCenter = hexMaze.getCellCenters()[cellIdx]; auto coord = Magnum::Vector3(cellCenter.first, 0.5f, cellCenter.second); auto offset = Magnum::Vector3(frand(envState.rng) - 0.5f, 0, frand(envState.rng) - 0.5f); coord += offset; objectCoordinates.emplace_back(coord.x() * mazeScale, coord.y(), coord.z() * mazeScale); } std::shuffle(objectCoordinates.begin(), objectCoordinates.end(), envState.rng); float cellWithObjectsFraction = frand(envState.rng) * 0.25f + 0.2f; long numCellsWithGoodObjects = std::lround(ceilf(cellWithObjectsFraction * objectCoordinates.size())); long numCellsWithBadObjects = std::lround(ceilf(cellWithObjectsFraction * objectCoordinates.size())); goodObjects = std::vector(objectCoordinates.begin(), objectCoordinates.begin() + numCellsWithGoodObjects); if (int(objectCoordinates.size()) >= numCellsWithGoodObjects + numCellsWithBadObjects) badObjects = std::vector(objectCoordinates.begin() + numCellsWithGoodObjects, objectCoordinates.begin() + numCellsWithGoodObjects + numCellsWithBadObjects); } void HexMemoryScenario::step() { constexpr auto collectRadius = 1.0f; if (goodObjectsCollected >= int(goodObjects.size()) && !solved) { solved = true; doneWithTimer(); } for (int i = 0; i < env.getNumAgents(); ++i) { auto agent = envState.agents[i]; const auto t = agent->absoluteTransformation().translation(); const auto agentCoords = vg.grid.getCoords(t); // checking the surrounding voxels for objects we can collect // (that's a lot of code, isn't it easier to just check all the objects globally lol) for (int dx = -1; dx <= 1; ++dx) for (int dz = -1; dz <= 1; ++dz) { const VoxelCoords coords{agentCoords.x() + dx, agentCoords.y(), agentCoords.z() + dz}; if (!vg.grid.hasVoxel(coords)) continue; const auto voxel = vg.grid.get(coords); for (auto it = voxel->objects.begin(); it != voxel->objects.end();) { const auto pillarPos = it->object->absoluteTransformation().translation(); const auto distance = (pillarPos - t).length(); if (distance < collectRadius) { // collecting the object rewardTeam(it->good ? Str::memoryCollectGood : Str::memoryCollectBad, i, 1); goodObjectsCollected += it->good; it->object->translate({100, 100, 100}); it = voxel->objects.erase(it); } else ++it; } } } } std::vector HexMemoryScenario::agentStartingPositions() { const auto rotationBetweenAgents = float(2 * M_PI / env.getNumAgents()); std::vector pos(env.getNumAgents()); for (int i = 0; i < env.getNumAgents(); ++i) pos[i] = 1.5f * Magnum::Vector3{sinf(rotationBetweenAgents * float(i)), 0.3, cosf(rotationBetweenAgents * float(i))}; return pos; } void HexMemoryScenario::spawnAgents(std::vector &agents) { const auto numAgents = env.getNumAgents(); const auto verticalLookLimitRad = floatParams[Str::verticalLookLimitRad]; const auto agentPositions = agentStartingPositions(); const auto rotationBetweenAgents = float(2 * M_PI / env.getNumAgents()); for (int i = 0; i < numAgents; ++i) { auto randomRotation = rotationBetweenAgents * i; auto &agent = envState.scene->addChild( envState.scene.get(), envState.physics->bWorld, Magnum::Vector3{agentPositions[i]} + Magnum::Vector3{0.5, 0.0, 0.5}, randomRotation, verticalLookLimitRad ); agent.updateTransform(); agents.emplace_back(&agent); } } void HexMemoryScenario::addEpisodeDrawables(DrawablesMap &drawables) { enum ShapeType { SHAPE_PILLAR, SHAPE_DIAMOND, SHAPE_SPHERE, }; const static std::vector shapes = {SHAPE_PILLAR, SHAPE_DIAMOND, SHAPE_SPHERE}; auto goodObjectColor = randomObjectColor(envState.rng), badObjectColor = goodObjectColor; auto goodShape = randomSample(shapes, envState.rng), badShape = goodShape; while (badObjectColor == goodObjectColor && badShape == goodShape) { badObjectColor = randomObjectColor(envState.rng); badShape = randomSample(shapes, envState.rng); } maze.addDrawablesAndCollisions(drawables, envState); auto addObject = [&](ShapeType shape, ColorRgb color, const Magnum::Vector3 &loc, const Magnum::Vector3 &scale) -> Object3D * { switch (shape) { case SHAPE_SPHERE: return addSphere(drawables, *envState.scene, loc, scale, color); case SHAPE_DIAMOND: return addDiamond(drawables, *envState.scene, loc, scale, color); case SHAPE_PILLAR: default: return addPillar(drawables, *envState.scene, loc, scale, color); } }; std::map scale { {SHAPE_SPHERE, {0.75, 0.75, 0.75}}, {SHAPE_PILLAR, {0.5, 2, 0.5}}, {SHAPE_DIAMOND, Vector3 {0.17f, 0.45f, 0.17f} * 2.2}, }; std::map shift { {SHAPE_SPHERE, {0.5, 0.1, 0.5}}, {SHAPE_PILLAR, {0.5, 0.05, 0.5}}, {SHAPE_DIAMOND, {0.5, 0.6, 0.5}}, }; // adding landmark object addObject(goodShape, goodObjectColor, landmarkLocation + shift[goodShape], scale[goodShape]); float objScale = 0.6; bool isGood = true; for (const auto &objects : {goodObjects, badObjects}) { for (const auto &coord : objects) { auto shape = isGood ? goodShape : badShape; auto color = isGood ? goodObjectColor : badObjectColor; const auto object = addObject(shape, color, coord + shift[shape] * objScale, scale[shape] * objScale); const auto voxelCoord = vg.grid.getCoords(coord); if (!vg.grid.hasVoxel(voxelCoord)) vg.grid.set(voxelCoord, VoxelHexMemory{}); vg.grid.get(voxelCoord)->objects.emplace_back(CollectableObject{object, isGood}); } isGood = !isGood; } } ================================================ FILE: src/libs/scenarios/src/scenario_obstacles.cpp ================================================ #include #include #include using namespace Magnum; using namespace Magnum::Math::Literals; using namespace Megaverse; namespace { std::unique_ptr makePlatform( const std::vector &platformTypes, Object3D *parent, Rng &rng, int walls, const FloatParams ¶ms, int width ) { const auto platformType = randomSample(platformTypes, rng); switch (platformType) { case PlatformType::STEP: return std::make_unique(parent, rng, walls, params, width); case PlatformType::GAP: return std::make_unique(parent, rng, walls, params, width); case PlatformType::LAVA: return std::make_unique(parent, rng, walls, params, width); case PlatformType::WALL: return std::make_unique(parent, rng, walls, params, width); case PlatformType::EMPTY: default: return std::make_unique(parent, rng, walls, params, width); } } } ObstaclesScenario::ObstaclesScenario(const std::string &name, Env &env, Env::EnvState &envState) : DefaultScenario(name, env, envState) , vg{*this} , platformsComponent{*this} , objectStackingComponent{*this, env.getNumAgents(), vg.grid, *this} , fallDetection{*this, vg.grid, *this} { } void ObstaclesScenario::reset() { vg.reset(env, envState); platformsComponent.reset(env, envState); objectStackingComponent.reset(env, envState); fallDetection.reset(env, envState); agentSpawnPositions.clear(), objectSpawnPositions.clear(), rewardSpawnPositions.clear(); agentReachedExit = std::vector(env.getNumAgents(), false); solved = false; auto &platforms = platformsComponent.platforms; const bool drawWalls = randRange(0, 2, envState.rng); StartPlatform *startPlatform = nullptr; // generating the level layout for (int attempt = 0; attempt < 20; ++attempt) { platforms.clear(); numPlatforms = randRange( int(lround(floatParams[Str::obstaclesMinNumPlatforms])), int(lround(floatParams[Str::obstaclesMaxNumPlatforms])) + 1, envState.rng ); static const std::vector orientations = {ORIENTATION_STRAIGHT, ORIENTATION_TURN_LEFT, ORIENTATION_TURN_RIGHT}; auto startPlatformPtr = std::make_unique(platformsComponent.levelRoot.get(), envState.rng, floatParams); startPlatformPtr->init(), startPlatformPtr->generate(); int requiredWidth = startPlatformPtr->width; startPlatform = startPlatformPtr.get(); Platform *previousPlatform = startPlatform; platformsComponent.addPlatform(std::move(startPlatformPtr)); int numMaxDifficultyObstacles = 0; int numAllowedMaxDifficultyObstacles = int(floatParams.at(Str::obstaclesNumAllowedMaxDifficulty)); for (int i = 0; i < numPlatforms; ++i) { auto orientation = randomSample(orientations, envState.rng); requiredWidth = orientation == ORIENTATION_STRAIGHT ? requiredWidth : -1; std::unique_ptr newPlatform; while (!newPlatform || (newPlatform->isMaxDifficulty() && numMaxDifficultyObstacles >= numAllowedMaxDifficultyObstacles)) { newPlatform = makePlatform(platformTypes, previousPlatform->nextPlatformAnchor, envState.rng, WALLS_WEST | WALLS_EAST, floatParams, requiredWidth); newPlatform->init(); } if (newPlatform->isMaxDifficulty()) { // TLOG(INFO) << "Max difficulty obstacle!"; ++numMaxDifficultyObstacles; } platformsComponent.addPlatform(std::move(newPlatform)); auto platform = platforms.back().get(); platform->generate(); switch (orientation) { case ORIENTATION_STRAIGHT: break; case ORIENTATION_TURN_LEFT: platform->rotateCCW(previousPlatform->width); break; case ORIENTATION_TURN_RIGHT: platform->rotateCW(previousPlatform->width); break; default: break; } if (orientation != ORIENTATION_STRAIGHT) { int walls = WALLS_NORTH; walls |= orientation == ORIENTATION_TURN_LEFT ? WALLS_WEST : WALLS_EAST; const int w = previousPlatform->width, l = platform->width - 1; platformsComponent.addPlatform(std::make_unique(previousPlatform->nextPlatformAnchor, envState.rng, walls, floatParams, l, w)); auto transitionPlatform = platforms.back().get(); transitionPlatform->init(); transitionPlatform->generate(); } previousPlatform = platform; requiredWidth = platform->width; } auto exitPlatformPtr = std::make_unique(previousPlatform->nextPlatformAnchor, envState.rng, floatParams, requiredWidth); exitPlatformPtr->init(), exitPlatformPtr->generate(); platformsComponent.addPlatform(std::move(exitPlatformPtr)); // don't check collisions with self and with the previous platforms (there might be overlap) bool selfCollision = false; for (int j = 0; j < int(platforms.size()) && !selfCollision; ++j) { for (int k = 0; k < j - 2; ++k) { if (platforms[j]->collidesWith(*platforms[k])) { TLOG(INFO) << "Platform " << j << " collides with " << k; selfCollision = true; break; } } } if (selfCollision) TLOG(INFO) << "Self collision! Attempt " << attempt << " re-generate!"; else break; } auto layoutColor = randomLayoutColor(envState.rng); auto wallColor = randomLayoutColor(envState.rng); for (auto &p : platforms) vg.addPlatform(*p, layoutColor, wallColor, drawWalls); assert(startPlatform); agentSpawnPositions = startPlatform->agentSpawnPoints(env.getNumAgents()); fallDetection.agentInitialPositions = agentSpawnPositions; // generating movable boxes std::vector numBoxes(platforms.size()); for (int i = 1; i < int(platforms.size()); ++i) { const auto n = platforms[i]->requiresMovableBoxesToTraverse(); for (int box = 0; box < n; ++box) { const auto platformIdx = randRange(std::max(0, i - 2), i, envState.rng); ++numBoxes[platformIdx]; } } for (int i = 0; i < int(platforms.size()); ++i) { float randomBoxesFraction = frand(envState.rng) * 0.5f; auto randomBoxes = int(lround(randomBoxesFraction * numBoxes[i])) + randRange(0, 2, envState.rng); const auto coords = platforms[i]->generateObjectPositions(numBoxes[i] + randomBoxes); objectSpawnPositions.insert(objectSpawnPositions.end(), coords.cbegin(), coords.cend()); } for (int i = 1; i < int(platforms.size()) - 1; ++i) { auto numRewardObjects = randRange(0, 2, envState.rng); const auto coords = platforms[i]->generateObjectPositions(numRewardObjects); rewardSpawnPositions.insert(rewardSpawnPositions.end(), coords.cbegin(), coords.cend()); } } void ObstaclesScenario::step() { objectStackingComponent.step(env, envState); fallDetection.step(env, envState); int numAgentsAtExit = 0; for (int i = 0; i < env.getNumAgents(); ++i) { auto agent = envState.agents[i]; const auto t = agent->absoluteTransformation().translation(); const auto voxel = vg.grid.getCoords(t); if (vg.grid.hasVoxel(voxel)) { const auto terrainType = vg.grid.get(voxel)->terrain; if (terrainType & TERRAIN_EXIT) { ++numAgentsAtExit; if (!agentReachedExit[i]) { agentReachedExit[i] = true; rewardTeam(Str::obstaclesAgentAtExit, i, 1); if (objectStackingComponent.agentCarryingObject(i)) { rewardTeam(Str::obstaclesAgentCarriedObjectToExit, i, 1); // TLOG(INFO) << "Carried object to exit"; } } } else if (terrainType & TERRAIN_LAVA) agentTouchedLava(i); // additional reward objects promote exploration auto voxelData = vg.grid.get(voxel); if (voxelData->rewardObject) { voxelData->rewardObject->translate({1000, 1000, 1000}); voxelData->rewardObject = nullptr; // remove the reference, but the object will be later cleaned when we destroy the scene graph rewardTeam(Str::obstaclesExtraReward, i, 1); } } } if (numAgentsAtExit == env.getNumAgents() && !solved) { solved = true; doneWithTimer(); rewardAll(Str::obstaclesAllAgentsAtExit, 1); } } void ObstaclesScenario::addEpisodeDrawables(DrawablesMap &drawables) { addDrawablesAndCollisionObjectsFromVoxelGrid(vg, drawables, envState, 1); // add terrains for (auto &platform : platformsComponent.platforms) for (auto &[terrainType, boxes] : platform->terrainBoxes) for (auto &bb : boxes) addTerrain(drawables, envState, terrainType, bb.boundingBox()); objectStackingComponent.addDrawablesAndCollisions(drawables, envState, objectSpawnPositions); for (const auto &pos : rewardSpawnPositions) { auto rewardObject = addDiamond(drawables, *envState.scene, Vector3{pos} + Vector3{0.5, 0.7, 0.5}, Vector3{0.17f, 0.45f, 0.17f} * 0.8f, ColorRgb::GREEN); if (!vg.grid.hasVoxel(pos)) vg.grid.set(pos, makeVoxel(VOXEL_EMPTY)); vg.grid.get(pos)->rewardObject = rewardObject; } } float ObstaclesScenario::episodeLengthSec() const { const auto minDuration = Scenario::episodeLengthSec(); return std::max(minDuration, float(numPlatforms) * 35 + float(objectSpawnPositions.size()) * 1); } void ObstaclesScenario::agentFell(int) { // we don't penalize the agent for stepping onto lava or falling // otherwise they get discouraged and never even go near these obstacles } void ObstaclesScenario::agentTouchedLava(int agentIdx) { fallDetection.resetAgent(agentIdx, envState.agents[agentIdx]); agentFell(agentIdx); } ================================================ FILE: src/libs/scenarios/src/scenario_rearrange.cpp ================================================ #include #include #include using namespace Magnum; using namespace Megaverse; class RearrangeScenario::RearrangePlatform : public EmptyPlatform { public: explicit RearrangePlatform(Object3D *parent, Rng &rng, int walls, const FloatParams ¶ms, int) : EmptyPlatform(parent, rng, walls, params) { } ~RearrangePlatform() override = default; void init() override { height = randRange(4, 7, rng); length = 19; width = 14; } void generate() override { EmptyPlatform::generate(); } }; RearrangeScenario::RearrangeScenario(const std::string &name, Env &env, Env::EnvState &envState) : DefaultScenario(name, env, envState) , platformsComponent{*this} , vg{*this, 100, 0, 0, 0, 1} , objectStackingComponent{*this, env.getNumAgents(), vg.grid, *this} { } RearrangeScenario::~RearrangeScenario() = default; void RearrangeScenario::reset() { solved = false; vg.reset(env, envState); objectStackingComponent.reset(env, envState); platformsComponent.reset(env, envState); platform = std::make_unique(platformsComponent.levelRoot.get(), envState.rng, WALLS_ALL, floatParams, env.getNumAgents()); platform->init(), platform->generate(); vg.addPlatform(*platform, ColorRgb::DARK_GREY, ColorRgb::DARK_GREY, randomBool(envState.rng)); arrangement = Arrangement{}; arrangementObjects.clear(); generateArrangement(); } void RearrangeScenario::generateArrangement() { const int arrangementSize = randRange(2, 8, envState.rng); // bfs std::queue q; std::unordered_set used; const auto firstItem = ArrangementItem::random(envState.rng, {0, 0, 0}); q.push(firstItem); arrangement.items.emplace_back(firstItem); used.insert({0, 0, 0}); std::vector directions{ {-1, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}, }; while (!q.empty()) { const auto currItem = q.front(); q.pop(); int maxBranches = randRange(1, int(directions.size()) + 1, envState.rng); maxBranches = randRange(1, maxBranches + 1, envState.rng); int numBranches = 0; std::shuffle(directions.begin(), directions.end(), envState.rng); for (auto dir : directions) { const auto newOffset = currItem.offset + dir; const auto below = newOffset - VoxelCoords {0, 1, 0}; if (newOffset.y() >= 2 || abs(newOffset.x()) >= 2 || abs(newOffset.z()) >= 2) continue; if (used.count(newOffset)) continue; // item has to be on the floor or on top of another item if (!(newOffset.y() == 0 || used.count(below))) continue; const auto newItem = ArrangementItem::random(envState.rng, newOffset); q.push(newItem); arrangement.items.emplace_back(newItem); used.insert(newOffset); ++numBranches; if (numBranches >= maxBranches) break; if (int(arrangement.items.size()) >= arrangementSize) break; } if (int(arrangement.items.size()) >= arrangementSize) break; } } void RearrangeScenario::step() { objectStackingComponent.step(env, envState); } bool RearrangeScenario::canPlaceObject(int, const VoxelCoords &coord, Object3D *) { const auto delta = coord - rightCenter; return abs(delta.x()) <= 2 && abs(delta.z()) <= 2; } int RearrangeScenario::countMatchingObjects() const { int matching = 0; for (const auto *obj : arrangementObjects) { if (obj->pickedUp) continue; const auto voxelCoords = vg.grid.getCoords(obj->absoluteTransformation().translation()); const auto offset = voxelCoords - rightCenter; if (arrangement.contains(obj->arrangementItem.shape, obj->arrangementItem.color, offset)) ++matching; } return matching; } void RearrangeScenario::placedObject(int agentIdx, const VoxelCoords &, Object3D *obj) { dynamic_cast(obj)->pickedUp = false; checkDone(agentIdx); } void RearrangeScenario::pickedObject(int agentIdx, const VoxelCoords &, Object3D *obj) { dynamic_cast(obj)->pickedUp = true; checkDone(agentIdx); } void RearrangeScenario::checkDone(int agentIdx) { const auto matches = countMatchingObjects(); if (matches > maxMatchingObjects) { rewardTeam(Str::rearrangeOneMoreObjectCorrectPosition, agentIdx, 1); maxMatchingObjects = matches; } if (matches >= int(arrangement.items.size()) && !solved) { solved = true; rewardTeam(Str::rearrangeAllObjectsCorrectPosition, agentIdx, 1); doneWithTimer(); } } std::vector RearrangeScenario::agentStartingPositions() { auto positions = std::vector(env.getNumAgents()); for (int i = 0; i < env.getNumAgents(); ++i) { for (int attempt = 0; attempt < 20; ++attempt) { int agentX = randRange(2, platform->length - 1, envState.rng); int agentZ = randRange(2, platform->width - 1, envState.rng); if (fabs(agentX - leftCenter.x()) < 2 && fabs(agentZ - leftCenter.z()) < 2) continue; if (fabs(agentX - rightCenter.x()) < 2 && fabs(agentZ - rightCenter.z()) < 2) continue; positions[i] = Vector3 {float(agentX), 2, float(agentZ)}; break; } } return positions; } void RearrangeScenario::arrangementDrawables(DrawablesMap &drawables, const Arrangement &arr, VoxelCoords center, bool interactive) { const static std::map scales { { DrawableType::Sphere, {1, 1, 1}}, { DrawableType::Box, {1, 1, 1}}, { DrawableType::Capsule, {0.8, 0.5, 0.8}}, { DrawableType::Cylinder, {0.9, 2, 0.9}}, }; const auto objSize = 0.45f; std::unordered_set occupied; for (const auto &item : arr.items) occupied.insert(item.offset); int numUnmovedItems = arr.items.size(); if (interactive) numUnmovedItems = randRange(0, arr.items.size(), envState.rng); int placedItems = 0; for (const auto &item : arr.items) { auto pos = item.offset + center; if (interactive && placedItems >= numUnmovedItems) { // random offset VoxelCoords newOffset = item.offset; while (occupied.count(newOffset)) newOffset = VoxelCoords{randRange(-2, 3, envState.rng), 0, randRange(-2, 3, envState.rng)}; pos = newOffset + center; occupied.insert(newOffset); } auto translation = Magnum::Vector3{float(pos.x()) + 0.5f, float(pos.y()) + 0.5f, float(pos.z()) + 0.5f}; auto bBoxShape = std::make_unique(btVector3{1, 1, 1}); auto &object = envState.scene->addChild(envState.scene.get(), 0.0f, bBoxShape.get(), envState.physics->bWorld); object.arrangementItem = item; object.scale(scales.at(item.shape) * objSize).translate(translation); if (item.shape == DrawableType::Cylinder) object.setCollisionScale({1, 0.5, 1}); else if (item.shape == DrawableType::Capsule) object.setCollisionScale({1, 2, 1}); object.syncPose(); drawables[item.shape].emplace_back(&object, rgb(item.color)); envState.physics->collisionShapes.emplace_back(std::move(bBoxShape)); if (interactive) { VoxelRearrange voxelState; voxelState.physicsObject = &object; vg.grid.set(pos, voxelState); arrangementObjects.emplace_back(&object); } ++placedItems; } } void RearrangeScenario::addEpisodeDrawables(DrawablesMap &drawables) { addDrawablesAndCollisionObjectsFromVoxelGrid(vg, drawables, envState, 1); for (int dx = -3; dx <= 3; ++dx) for (int dz = -3; dz <= 3; ++dz) { VoxelRearrange voxelState; voxelState.voxelType = VOXEL_SOLID; vg.grid.set(VoxelCoords {leftCenter.x() + dx, 1, leftCenter.z() + dz}, voxelState); vg.grid.set(VoxelCoords {rightCenter.x() + dx, 1, rightCenter.z() + dz}, voxelState); } arrangementDrawables(drawables, arrangement, leftCenter, false); arrangementDrawables(drawables, arrangement, rightCenter, true); maxMatchingObjects = countMatchingObjects(); TLOG(DEBUG) << "Initial num matching objects: " << maxMatchingObjects; Vector3 platformCenter = {9.5, 0, 7}; addStaticCollidingBox(drawables, envState, {8.35, 0.5, 5.65}, Vector3{platformCenter} + Vector3 {0.0, 1, 0.0}, ColorRgb::DARK_GREY); // add pedestal for the desired arrangement addStaticCollidingBox(drawables, envState, {3, 0.5, 3}, Vector3{leftCenter} + Vector3 {0.5, -0.5, 0.5}, ColorRgb::LAYOUT_DEFAULT); addStaticCollidingBox(drawables, envState, {1.5, 0.5, 1.5}, Vector3{leftCenter} + Vector3 {0.5, -0.45, 0.5}, ColorRgb::DARK_GREY); addStaticCollidingBox(drawables, envState, {3, 0.5, 3}, Vector3{leftCenter} + Vector3 {1.0, -0.66, 1.0}, ColorRgb::LAYOUT_DEFAULT); addStaticCollidingBox(drawables, envState, {3, 0.5, 3}, Vector3{leftCenter} + Vector3 {1.5, -0.82, 1.5}, ColorRgb::LAYOUT_DEFAULT); // add pedestal for the working area addStaticCollidingBox(drawables, envState, {3, 0.5, 3}, Vector3{rightCenter} + Vector3 {0.5, -0.5, 0.5}, ColorRgb::BLUE); addStaticCollidingBox(drawables, envState, {1.5, 0.5, 1.5}, Vector3{rightCenter} + Vector3 {0.5, -0.45, 0.5}, ColorRgb::DARK_GREY); addStaticCollidingBox(drawables, envState, {3, 0.5, 3}, Vector3{rightCenter} + Vector3 {0, -0.66, 1.0}, ColorRgb::BLUE); addStaticCollidingBox(drawables, envState, {3, 0.5, 3}, Vector3{rightCenter} + Vector3 {-0.5, -0.82, 1.5}, ColorRgb::BLUE); } ================================================ FILE: src/libs/scenarios/src/scenario_sokoban.cpp ================================================ #include #include #include #include #include using namespace Megaverse; using namespace Magnum::Math::Literals; namespace { enum SokobanNotation { EMPTY_CELL = ' ', GOAL_CELL = '.', PLAYER_CELL = '@', PLAYER_ON_GOAL = '+', BOX_CELL = '$', BOX_ON_GOAL = '*', WALL_CELL = '#', }; enum SokobanTerrain { SOKO_EMPTY = 0, SOKO_WALL = 1, SOKO_GOAL = 2, }; } SokobanScenario::SokobanScenario(const std::string &name, Env &env, Env::EnvState &envState) : DefaultScenario(name, env, envState) , vg{*this, 100, 0, 0, 0, 2} { auto envvarBoxobanPath = std::getenv("BOXOBAN_LEVELS"); if (!envvarBoxobanPath || strlen(envvarBoxobanPath) == 0) TLOG(DEBUG) << "Could not find Boxoban levels through the environment variable 'BOXOBAN_LEVELS'"; else boxobanLevelsDir = envvarBoxobanPath; constexpr auto backupLocation = "~/datasets/boxoban"; if (boxobanLevelsDir.empty()) boxobanLevelsDir = backupLocation; if (contains(boxobanLevelsDir, '~')) { auto envvarHome = std::getenv("HOME"); if (!envvarHome || strlen(envvarHome) == 0) TLOG(FATAL) << "Could not query HOME env var to resolve ~ in the path to Boxoban levels dir"; boxobanLevelsDir = std::regex_replace(boxobanLevelsDir, std::regex("~"), envvarHome); } auto dirWithLevels = pathJoin(boxobanLevelsDir, levelSet, levelSplit); for (int levelFileIdx = 0; levelFileIdx <= 999; ++levelFileIdx) { std::ostringstream ss; ss << std::setw(3) << std::setfill('0') << levelFileIdx << ".txt"; auto levelFilePath = pathJoin(dirWithLevels, ss.str()); if (fileExists(levelFilePath)) allSokobanLevelFiles.emplace_back(levelFilePath); } if (allSokobanLevelFiles.empty()) TLOG(FATAL) << "Could not find any Boxoban levels. Set envvar BOXOBAN_LEVELS or put unzipped Boxoban folder " "(named boxoban) containing unfiltered/medium/hard level splits into ~/datasets"; TLOG(INFO) << allSokobanLevelFiles.size() << " boxoban level files found"; } SokobanScenario::~SokobanScenario() = default; void SokobanScenario::reloadLevels() { const auto levelFilePath = randomSample(allSokobanLevelFiles, envState.rng); std::vector buffer; const auto bytesRead = readAllBytes(levelFilePath, buffer); if (!bytesRead) TLOG(FATAL) << "Could not read the level file " << levelFilePath; std::string content{buffer.begin(), buffer.end()}; auto lines = splitString(content, "\n"); SokobanLevel level; for (int i = 0; i < int(lines.size()); ++i) { if (startsWith(lines[i], ";")) { if (i > 0) levels.emplace_back(std::move(level)); level = SokobanLevel{}; } else level.rows.emplace_back(lines[i]); } std::shuffle(levels.begin(), levels.end(), envState.rng); } void SokobanScenario::reset() { vg.reset(env, envState); solved = false; agentPositions.clear(), boxesCoords.clear(); length = width = 0; numBoxes = numBoxesOnGoal = 0; if (levels.empty()) reloadLevels(); currLevel = levels.back(); levels.pop_back(); createLayout(); } void SokobanScenario::createLayout() { constexpr int wallHeight = 2; auto &g = vg.grid; static const std::vector floorColors = { ColorRgb::LAYOUT_DEFAULT, ColorRgb::VERY_LIGHT_YELLOW, ColorRgb::VERY_LIGHT_BLUE, ColorRgb::VERY_LIGHT_ORANGE, ColorRgb::DARK_GREY, }; auto floorColor = randomSample(floorColors, envState.rng); length = int(currLevel.rows.size()); for (int x = 0; x < length; ++x) { const auto &row = currLevel.rows[x]; width = std::max(width, int(row.size())); for (int z = 0; z < int(row.size()); ++z) { g.set({x, 0, z}, makeVoxel(VOXEL_SOLID | VOXEL_OPAQUE, TERRAIN_NONE, floorColor)); if (row[z] == WALL_CELL) { for (int y = 1; y <= wallHeight; ++y) g.set({x, y, z}, makeVoxel(VOXEL_SOLID)); g.get(VoxelCoords{x, 1, z})->terrain = SOKO_WALL; } if (row[z] == PLAYER_CELL || row[z] == PLAYER_ON_GOAL) { for (int agentIdx = 0; agentIdx < env.getNumAgents(); ++agentIdx) { const float agentX = float(x) + float(agentIdx % 2) * 0.5f; const float agentZ = float(z) + float(agentIdx % 4 > 1) * 0.5f; agentPositions.emplace_back(agentX * voxelSize, voxelSize + 0.3 * float(agentIdx) * voxelSize, agentZ * voxelSize); } } if (row[z] == GOAL_CELL || row[z] == PLAYER_ON_GOAL) g.set({x, 1, z}, makeVoxel(VOXEL_EMPTY, SOKO_GOAL)); if (row[z] == BOX_CELL || row[z] == BOX_ON_GOAL) { boxesCoords.emplace_back(x, 1, z); ++numBoxes; } } } } void SokobanScenario::step() { // TODO: in multi-agent envs they can push each other and thus move unmovable boxes. Fix // moving the boxes logic for (int i = 0; i < env.getNumAgents(); ++i) { const auto a = envState.currAction[i]; auto &agent = envState.agents[i]; if (!!(a & Action::Interact)) { auto t = agent->interactLocation()->absoluteTransformation().translation(); auto voxel = vg.grid.getWithVector(t); if (voxel && voxel->physicsObject) { const auto boxPos = vg.grid.getCoords(t); const auto agentPos = vg.grid.getCoords(agent->absoluteTransformation().translation()); const auto dist = manhattanDistance(agentPos, boxPos); if (dist == 1) { // only move the box if we are in the adjacent cell auto deltaPos = boxPos - agentPos; auto desiredPos = boxPos + deltaPos; bool occupied = false; for (int j = 0; j < env.getNumAgents(); ++j) if (vg.grid.getCoords(envState.agents[j]->absoluteTransformation().translation()) == desiredPos) { occupied = true; break; } if (!occupied) { if (!vg.grid.hasVoxel(desiredPos)) vg.grid.set(desiredPos, makeVoxel(VOXEL_EMPTY)); auto desiredPosVoxel = vg.grid.get(desiredPos); if (desiredPosVoxel->terrain != SOKO_WALL && !desiredPosVoxel->physicsObject) { desiredPosVoxel->physicsObject = voxel->physicsObject; desiredPosVoxel->physicsObject->parent()->translate(Magnum::Vector3{deltaPos} * voxelSize); desiredPosVoxel->physicsObject->syncPose(); voxel->physicsObject = nullptr; // moved the box if (voxel->terrain != SOKO_GOAL && desiredPosVoxel->terrain == SOKO_GOAL) { // moved the box to the goal position ++numBoxesOnGoal; rewardTeam(Str::sokobanBoxOnTarget, i, 1); if (numBoxesOnGoal == numBoxes && !solved) { TLOG(INFO) << "Done!"; solved = true; rewardTeam(Str::sokobanAllBoxesOnTarget, i, 1); doneWithTimer(); } } else if (voxel->terrain == SOKO_GOAL && desiredPosVoxel->terrain != SOKO_GOAL) { // moved the box from the goal position - penalty --numBoxesOnGoal; rewardTeam(Str::sokobanBoxLeavesTarget, i, 1); } } } } } } } } std::vector SokobanScenario::agentStartingPositions() { return agentPositions; } void SokobanScenario::addEpisodeDrawables(DrawablesMap &drawables) { addDrawablesAndCollisionObjectsFromVoxelGrid(vg, drawables, envState, vg.grid.getVoxelSize()); const static std::map colors = { {SOKO_WALL, ColorRgb::LIGHT_ORANGE}, {SOKO_GOAL, ColorRgb::LIGHT_GREEN}, }; const static std::map height = { {SOKO_WALL, 0.35f}, {SOKO_GOAL, 0.025f}, }; auto &g = vg.grid; for (int x = 0; x < length; ++x) { for (int z = 0; z < width; ++z) { if (!g.hasVoxel({x, 1, z})) continue; auto v = g.get({x, 1, z}); if (v->terrain == SOKO_EMPTY) continue; const auto pos = Magnum::Vector3(voxelSize * float(x) + voxelSize / 2, voxelSize, voxelSize * float(z) + voxelSize / 2); const auto h = height.at(SokobanTerrain(v->terrain)); auto &terrainObject = envState.scene->addChild(envState.scene.get()); terrainObject.scale({1, h, 1}); terrainObject.translate({0.0, h, 0.0}); terrainObject.translate(pos); drawables[DrawableType::Box].emplace_back(&terrainObject, rgb(colors.at(SokobanTerrain(v->terrain)))); } } for (auto box : boxesCoords) { auto scale = Magnum::Vector3{voxelSize / 2, 0.45f, voxelSize / 2} * 0.8f; auto translation = Magnum::Vector3{float(box.x()) + 0.5f, float(box.y()) + 0.2f, float(box.z()) + 0.5f} * voxelSize; auto &layoutBox = envState.scene->addChild(); layoutBox.scale(scale).translate(translation); drawables[DrawableType::Box].emplace_back(&layoutBox, rgb(ColorRgb::DARK_BLUE)); auto bBoxShape = std::make_unique(btVector3{1, 1, 1}); auto &collisionBox = layoutBox.addChild(envState.scene.get(), 0.0f, bBoxShape.get(), envState.physics->bWorld); collisionBox.setCollisionScale({1.15, 3, 1.15}); collisionBox.setCollisionOffset({0, 0.6, 0}); collisionBox.syncPose(); envState.physics->collisionShapes.emplace_back(std::move(bBoxShape)); if (!g.hasVoxel({box})) g.set(box, makeVoxel(VOXEL_EMPTY)); g.get(box)->physicsObject = &collisionBox; } } ================================================ FILE: src/libs/scenarios/src/scenario_tower_building.cpp ================================================ #include #include using namespace Megaverse; class TowerBuildingScenario::TowerBuildingPlatform : public EmptyPlatform { public: explicit TowerBuildingPlatform(Object3D *parent, Rng &rng, int walls, const FloatParams ¶ms, int numAgents) : EmptyPlatform(parent, rng, walls, params) , numAgents{numAgents} { } ~TowerBuildingPlatform() override = default; void init() override { height = randRange(5, 7, rng); length = randRange(12, 30, rng); width = randRange(12, 25, rng); // determine the size and the position of the building zone buildZoneLength = randRange(3, 9, rng); buildZoneWidth = randRange(3, 9, rng); materialsLength = randRange(2, 8, rng); materialsWidth = randRange(2, 8, rng); length = std::max(buildZoneLength + materialsLength + 3, length); width = std::max(buildZoneWidth + materialsWidth + 3, width); buildZoneXOffset = randRange(1, length - buildZoneLength - 1, rng); buildZoneZOffset = randRange(1, width - buildZoneWidth - 1, rng); materialsXOffset = randRange(1, length - materialsLength - 1, rng); materialsZOffset = randRange(1, width - materialsWidth - 1, rng); std::vector spawnCandidates; for (int x = 1; x < length - 1; ++x) for (int z = 1; z < width - 1; ++z) spawnCandidates.emplace_back(x, 2, z); std::shuffle(spawnCandidates.begin(), spawnCandidates.end(), rng); agentSpawnCoords = toFloat(std::vector( spawnCandidates.begin(), spawnCandidates.begin() + std::min(numAgents, int(spawnCandidates.size())) )); auto spawnIdx = int(agentSpawnCoords.size()); const auto maxRandomObjects = std::min(int(spawnCandidates.size()) - numAgents, 25); const auto spawnObjects = randRange(0, std::max(1, maxRandomObjects), rng); // spawn a bunch of random objects objectSpawnCoords = std::vector( spawnCandidates.begin() + spawnIdx, spawnCandidates.begin() + spawnIdx + spawnObjects ); // objects that are within the "materials" rectangle will be at y=2, otherwise drop them on the floor for (auto &c : objectSpawnCoords) { if (c.x() >= materialsXOffset && c.x() < materialsXOffset + materialsLength && c.z() >= materialsZOffset && c.z() < materialsZOffset + materialsWidth) { continue; } c.y() -= 1; // put the object on the floor } // add the main bulk of materials, a random rectangle of boxes for (int x = materialsXOffset; x < materialsXOffset + materialsLength; ++x) for (int y = 1; y <= 1; ++y) for (int z = materialsZOffset; z < materialsZOffset + materialsWidth; ++z) objectSpawnCoords.emplace_back(x, y, z); while (int(agentSpawnCoords.size()) < numAgents) agentSpawnCoords.emplace_back(agentSpawnCoords[0]); } void generate() override { EmptyPlatform::generate(); const VoxelCoords minCoord{buildZoneXOffset, 1, buildZoneZOffset}, maxCoord{buildZoneXOffset + buildZoneLength, 1, buildZoneZOffset + buildZoneWidth}; MagnumAABB buildingZoneBB{*root, {minCoord, maxCoord}}; terrainBoxes[TERRAIN_BUILDING_ZONE].emplace_back(buildingZoneBB); } std::vector agentSpawnPoints(int /*numAgents*/) override { return agentSpawnCoords; } std::vector generateObjectPositions(int /*numBoxesToGenerate*/) override { return objectSpawnCoords; } int numMovableBoxes() const { return objectSpawnCoords.size(); } private: std::vector agentSpawnCoords; std::vector objectSpawnCoords; int buildZoneLength{}, buildZoneWidth{}, materialsLength{}, materialsWidth{}; int buildZoneXOffset{}, buildZoneZOffset{}, materialsXOffset{}, materialsZOffset{}; int numAgents{}; }; TowerBuildingScenario::TowerBuildingScenario(const std::string &name, Env &env, Env::EnvState &envState) : DefaultScenario{name, env, envState} , vg{*this} , objectStackingComponent{*this, env.getNumAgents(), vg.grid, *this} , fallDetection{*this, vg.grid, *this} , platformsComponent{*this} , agentState(size_t(env.getNumAgents())) { } TowerBuildingScenario::~TowerBuildingScenario() = default; void TowerBuildingScenario::reset() { objectStackingComponent.reset(env, envState); vg.reset(env, envState); fallDetection.reset(env, envState); platformsComponent.reset(env, envState); std::fill(agentState.begin(), agentState.end(), AgentState{}); previousReward.clear(); auto layoutColor = randomLayoutColor(envState.rng); while (layoutColor == ColorRgb::BUILDING_ZONE) layoutColor = randomLayoutColor(envState.rng); platform = std::make_unique(platformsComponent.levelRoot.get(), envState.rng, WALLS_ALL, floatParams, env.getNumAgents()); platform->init(), platform->generate(); vg.addPlatform(*platform, layoutColor, randomLayoutColor(envState.rng), randomBool(envState.rng)); buildingZone = platform->terrainBoxes[TERRAIN_BUILDING_ZONE].front().boundingBox(); currBuildingZoneReward = 0.0f; objectsInBuildingZone.clear(); highestTower = 0; fallDetection.agentInitialPositions = agentStartingPositions(); } std::vector TowerBuildingScenario::agentStartingPositions() { return platform->agentSpawnPoints(env.getNumAgents()); } void TowerBuildingScenario::addEpisodeDrawables(DrawablesMap &drawables) { addDrawablesAndCollisionObjectsFromVoxelGrid(vg, drawables, envState, 1); for (auto &[terrainType, boxes] : platform->terrainBoxes) for (auto &bb : boxes) addTerrain(drawables, envState, terrainType, bb.boundingBox()); const auto objectPositions = platform->generateObjectPositions(-1); for (const auto &pos : objectPositions) if (isInBuildingZone(pos)) objectsInBuildingZone.insert(pos); currBuildingZoneReward = calculateTowerReward(); TLOG(INFO) << "Initial tower reward: " << currBuildingZoneReward; objectStackingComponent.addDrawablesAndCollisions(drawables, envState, objectPositions); } void TowerBuildingScenario::step() { objectStackingComponent.step(env, envState); fallDetection.step(env, envState); for (int i = 0; i < env.getNumAgents(); ++i) { // reward shaping: give agents reward for visiting bulding zone while carrying the object if (objectStackingComponent.agentCarryingObject(i)) { const auto &agent = envState.agents[i]; const auto t = agent->transformation().translation(); VoxelCoords voxel = vg.grid.getCoords(t); if (isInBuildingZone(voxel)) { if (!agentState[i].visitedBuildingZoneWithObject) { rewardTeam(Str::towerVisitedBuildingZoneWithObject, i, 1); agentState[i].visitedBuildingZoneWithObject = true; } } } } } bool TowerBuildingScenario::canPlaceObject(int, const VoxelCoords &c, Object3D *) { return isInBuildingZone(c); } void TowerBuildingScenario::placedObject(int agentIdx, const VoxelCoords &voxel, Object3D *) { if (isInBuildingZone(voxel)) objectsInBuildingZone.insert(voxel); addCollectiveReward(agentIdx); highestTower = std::max(highestTower, voxel.y() - buildingZone.min.y() + 1); } void TowerBuildingScenario::pickedObject(int agentIdx, const VoxelCoords &voxel, Object3D *) { if (isInBuildingZone(voxel)) objectsInBuildingZone.erase(voxel); if (!agentState[agentIdx].pickedUpObject) { rewardAgent(Str::towerPickedUpObject, agentIdx, 1); agentState[agentIdx].pickedUpObject = true; } } bool TowerBuildingScenario::isInBuildingZone(const VoxelCoords &c) const { return c.x() >= buildingZone.min.x() && c.x() < buildingZone.max.x() && c.z() >= buildingZone.min.z() && c.z() < buildingZone.max.z(); } float TowerBuildingScenario::calculateTowerReward() const { float reward = 0.0f; for (auto &pos : objectsInBuildingZone) { auto height = pos.y(); reward += buildingRewardCoeffForHeight(height); } return reward; } /** * The higher the agents place the blocks the more we reward them. */ float TowerBuildingScenario::buildingRewardCoeffForHeight(float height) { auto res = height * 0.05f; res += std::min(0.05f * powf(2, height), 20.0f); return res; } void TowerBuildingScenario::addCollectiveReward(int agentIdx) { auto newReward = calculateTowerReward(); auto rewardDelta = newReward - currBuildingZoneReward; // TLOG(INFO) << "Curr reward: " << rewardDelta << " new reward: " << newReward; currBuildingZoneReward = newReward; rewardTeam(Str::towerBuildingReward, agentIdx, rewardDelta); } float Megaverse::TowerBuildingScenario::episodeLengthSec() const { return Scenario::episodeLengthSec() + 4.0f * float(platform->numMovableBoxes()); } ================================================ FILE: src/libs/util/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.10) project(libutil VERSION 0.1 LANGUAGES CXX) add_library_default(util) target_link_libraries(util PUBLIC Magnum::Magnum) ================================================ FILE: src/libs/util/include/util/argparse.hpp ================================================ /* __ _ _ __ __ _ _ __ __ _ _ __ ___ ___ / _` | '__/ _` | '_ \ / _` | '__/ __|/ _ \ Argument Parser for Modern C++ | (_| | | | (_| | |_) | (_| | | \__ \ __/ http://github.com/p-ranav/argparse \__,_|_| \__, | .__/ \__,_|_| |___/\___| |___/|_| Licensed under the MIT License . SPDX-License-Identifier: MIT Copyright (c) 2019-2021 Pranav Srinivas Kumar and other contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace argparse { namespace details { // namespace for helper methods template struct is_container : std::false_type {}; template <> struct is_container : std::false_type {}; template struct is_container().begin()), decltype(std::declval().end()), decltype(std::declval().size())>> : std::true_type {}; template static constexpr bool is_container_v = is_container::value; template struct is_streamable : std::false_type {}; template struct is_streamable< T, std::void_t() << std::declval())>> : std::true_type {}; template static constexpr bool is_streamable_v = is_streamable::value; template static constexpr bool is_representable_v = is_streamable_v || is_container_v; constexpr std::size_t repr_max_container_size = 5; template std::string repr(T const &val) { if constexpr (std::is_same_v) { return val ? "true" : "false"; } else if constexpr (std::is_convertible_v) { return '"' + std::string{std::string_view{val}} + '"'; } else if constexpr (is_container_v) { std::stringstream out; out << "{"; const auto size = val.size(); if (size > 1) { out << repr(*val.begin()); std::for_each( std::next(val.begin()), std::next(val.begin(), std::min(size, repr_max_container_size) - 1), [&out](const auto &v) { out << " " << repr(v); }); if (size <= repr_max_container_size) out << " "; else out << "..."; } if (size > 0) out << repr(*std::prev(val.end())); out << "}"; return out.str(); } else if constexpr (is_streamable_v) { std::stringstream out; out << val; return out.str(); } else { return ""; } } namespace { template constexpr bool standard_signed_integer = false; template <> constexpr bool standard_signed_integer = true; template <> constexpr bool standard_signed_integer = true; template <> constexpr bool standard_signed_integer = true; template <> constexpr bool standard_signed_integer = true; template <> constexpr bool standard_signed_integer = true; template constexpr bool standard_unsigned_integer = false; template <> constexpr bool standard_unsigned_integer = true; template <> constexpr bool standard_unsigned_integer = true; template <> constexpr bool standard_unsigned_integer = true; template <> constexpr bool standard_unsigned_integer = true; template <> constexpr bool standard_unsigned_integer = true; } // namespace template constexpr bool standard_integer = standard_signed_integer || standard_unsigned_integer; template constexpr decltype(auto) apply_plus_one_impl(F &&f, Tuple &&t, Extra &&x, std::index_sequence) { return std::invoke(std::forward(f), std::get(std::forward(t))..., std::forward(x)); } template constexpr decltype(auto) apply_plus_one(F &&f, Tuple &&t, Extra &&x) { return details::apply_plus_one_impl( std::forward(f), std::forward(t), std::forward(x), std::make_index_sequence< std::tuple_size_v>>{}); } constexpr auto pointer_range(std::string_view s) noexcept { return std::tuple(s.data(), s.data() + s.size()); } template constexpr bool starts_with(std::basic_string_view prefix, std::basic_string_view s) noexcept { return s.substr(0, prefix.size()) == prefix; } enum class chars_format { scientific = 0x1, fixed = 0x2, hex = 0x4, general = fixed | scientific }; struct consume_hex_prefix_result { bool is_hexadecimal; std::string_view rest; }; using namespace std::literals; constexpr auto consume_hex_prefix(std::string_view s) -> consume_hex_prefix_result { if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) { s.remove_prefix(2); return {true, s}; } else { return {false, s}; } } template inline auto do_from_chars(std::string_view s) -> T { T x; auto [first, last] = pointer_range(s); auto [ptr, ec] = std::from_chars(first, last, x, Param); if (ec == std::errc()) { if (ptr == last) return x; else throw std::invalid_argument{"pattern does not match to the end"}; } else if (ec == std::errc::invalid_argument) { throw std::invalid_argument{"pattern not found"}; } else if (ec == std::errc::result_out_of_range) { throw std::range_error{"not representable"}; } else { return x; // unreachable } } template struct parse_number { auto operator()(std::string_view s) -> T { return do_from_chars(s); } }; template struct parse_number { auto operator()(std::string_view s) -> T { if (auto [ok, rest] = consume_hex_prefix(s); ok) return do_from_chars(rest); else throw std::invalid_argument{"pattern not found"}; } }; template struct parse_number { auto operator()(std::string_view s) -> T { if (auto [ok, rest] = consume_hex_prefix(s); ok) return do_from_chars(rest); else if (starts_with("0"sv, s)) return do_from_chars(rest); else return do_from_chars(rest); } }; namespace { template constexpr auto generic_strtod = nullptr; template <> constexpr auto generic_strtod = strtof; template <> constexpr auto generic_strtod = strtod; template <> constexpr auto generic_strtod = strtold; } // namespace template inline auto do_strtod(std::string const &s) -> T { if (isspace(static_cast(s[0])) || s[0] == '+') throw std::invalid_argument{"pattern not found"}; auto [first, last] = pointer_range(s); char *ptr; errno = 0; if (auto x = generic_strtod(first, &ptr); errno == 0) { if (ptr == last) return x; else throw std::invalid_argument{"pattern does not match to the end"}; } else if (errno == ERANGE) { throw std::range_error{"not representable"}; } else { return x; // unreachable } } template struct parse_number { auto operator()(std::string const &s) -> T { if (auto r = consume_hex_prefix(s); r.is_hexadecimal) throw std::invalid_argument{ "chars_format::general does not parse hexfloat"}; return do_strtod(s); } }; template struct parse_number { auto operator()(std::string const &s) -> T { if (auto r = consume_hex_prefix(s); !r.is_hexadecimal) throw std::invalid_argument{"chars_format::hex parses hexfloat"}; return do_strtod(s); } }; template struct parse_number { auto operator()(std::string const &s) -> T { if (auto r = consume_hex_prefix(s); r.is_hexadecimal) throw std::invalid_argument{ "chars_format::scientific does not parse hexfloat"}; if (s.find_first_of("eE") == s.npos) throw std::invalid_argument{ "chars_format::scientific requires exponent part"}; return do_strtod(s); } }; template struct parse_number { auto operator()(std::string const &s) -> T { if (auto r = consume_hex_prefix(s); r.is_hexadecimal) throw std::invalid_argument{ "chars_format::fixed does not parse hexfloat"}; if (s.find_first_of("eE") != s.npos) throw std::invalid_argument{ "chars_format::fixed does not parse exponent part"}; return do_strtod(s); } }; } // namespace details enum class default_arguments : unsigned int { none = 0, help = 1, version = 2, all = help | version, }; inline bool operator& (const default_arguments &a, const default_arguments &b) { return static_cast(a) & static_cast(b); } class ArgumentParser; class Argument { friend class ArgumentParser; friend auto operator<<(std::ostream &, ArgumentParser const &) -> std::ostream &; template explicit Argument(std::string_view(&&a)[N], std::index_sequence) : mIsOptional((is_optional(a[I]) || ...)), mIsRequired(false), mIsRepeatable(false), mIsUsed(false) { ((void)mNames.emplace_back(a[I]), ...); std::sort( mNames.begin(), mNames.end(), [](const auto &lhs, const auto &rhs) { return lhs.size() == rhs.size() ? lhs < rhs : lhs.size() < rhs.size(); }); } public: template explicit Argument(std::string_view(&&a)[N]) : Argument(std::move(a), std::make_index_sequence{}) {} Argument &help(std::string aHelp) { mHelp = std::move(aHelp); return *this; } template Argument &default_value(T &&aDefaultValue) { mDefaultValueRepr = details::repr(aDefaultValue); mDefaultValue = std::forward(aDefaultValue); return *this; } Argument &required() { mIsRequired = true; return *this; } Argument &implicit_value(std::any aImplicitValue) { mImplicitValue = std::move(aImplicitValue); mNumArgs = 0; return *this; } template auto action(F &&aAction, Args &&... aBound) -> std::enable_if_t, Argument &> { using action_type = std::conditional_t< std::is_void_v>, void_action, valued_action>; if constexpr (sizeof...(Args) == 0) mAction.emplace(std::forward(aAction)); else mAction.emplace( [f = std::forward(aAction), tup = std::make_tuple(std::forward(aBound)...)]( std::string const &opt) mutable { return details::apply_plus_one(f, tup, opt); }); return *this; } auto &append() { mIsRepeatable = true; return *this; } template auto scan() -> std::enable_if_t, Argument &> { static_assert(!(std::is_const_v || std::is_volatile_v), "T should not be cv-qualified"); auto is_one_of = [](char c, auto... x) constexpr { return ((c == x) || ...); }; if constexpr (is_one_of(Shape, 'd') && details::standard_integer) action(details::parse_number()); else if constexpr (is_one_of(Shape, 'i') && details::standard_integer) action(details::parse_number()); else if constexpr (is_one_of(Shape, 'u') && details::standard_unsigned_integer) action(details::parse_number()); else if constexpr (is_one_of(Shape, 'o') && details::standard_unsigned_integer) action(details::parse_number()); else if constexpr (is_one_of(Shape, 'x', 'X') && details::standard_unsigned_integer) action(details::parse_number()); else if constexpr (is_one_of(Shape, 'a', 'A') && std::is_floating_point_v) action(details::parse_number()); else if constexpr (is_one_of(Shape, 'e', 'E') && std::is_floating_point_v) action(details::parse_number()); else if constexpr (is_one_of(Shape, 'f', 'F') && std::is_floating_point_v) action(details::parse_number()); else if constexpr (is_one_of(Shape, 'g', 'G') && std::is_floating_point_v) action(details::parse_number()); else static_assert(alignof(T) == 0, "No scan specification for T"); return *this; } Argument &nargs(int aNumArgs) { if (aNumArgs < 0) throw std::logic_error("Number of arguments must be non-negative"); mNumArgs = aNumArgs; return *this; } Argument &remaining() { mNumArgs = -1; return *this; } template Iterator consume(Iterator start, Iterator end, std::string_view usedName = {}) { if (!mIsRepeatable && mIsUsed) { throw std::runtime_error("Duplicate argument"); } mIsUsed = true; mUsedName = usedName; if (mNumArgs == 0) { mValues.emplace_back(mImplicitValue); std::visit([](const auto &aAction) { aAction({}); }, mAction); return start; } else if (mNumArgs <= std::distance(start, end)) { if (auto expected = maybe_nargs()) { end = std::next(start, *expected); if (std::any_of(start, end, Argument::is_optional)) { throw std::runtime_error("optional argument in parameter sequence"); } } struct action_apply { void operator()(valued_action &f) { std::transform(first, last, std::back_inserter(self.mValues), f); } void operator()(void_action &f) { std::for_each(first, last, f); if (!self.mDefaultValue.has_value()) { if (auto expected = self.maybe_nargs()) self.mValues.resize(*expected); } } Iterator first, last; Argument &self; }; std::visit(action_apply{start, end, *this}, mAction); return end; } else if (mDefaultValue.has_value()) { return start; } else { throw std::runtime_error("Too few arguments for '" + std::string(mUsedName) + "'."); } } /* * @throws std::runtime_error if argument values are not valid */ void validate() const { if (auto expected = maybe_nargs()) { if (mIsOptional) { if (mIsUsed && mValues.size() != *expected && !mIsRepeatable && !mDefaultValue.has_value()) { std::stringstream stream; stream << mUsedName << ": expected " << *expected << " argument(s). " << mValues.size() << " provided."; throw std::runtime_error(stream.str()); } else { // TODO: check if an implicit value was programmed for this argument if (!mIsUsed && !mDefaultValue.has_value() && mIsRequired) { std::stringstream stream; stream << mNames[0] << ": required."; throw std::runtime_error(stream.str()); } if (mIsUsed && mIsRequired && mValues.size() == 0) { std::stringstream stream; stream << mUsedName << ": no value provided."; throw std::runtime_error(stream.str()); } } } else { if (mValues.size() != expected && !mDefaultValue.has_value()) { std::stringstream stream; if (!mUsedName.empty()) stream << mUsedName << ": "; stream << *expected << " argument(s) expected. " << mValues.size() << " provided."; throw std::runtime_error(stream.str()); } } } } auto maybe_nargs() const -> std::optional { if (mNumArgs < 0) return std::nullopt; else return static_cast(mNumArgs); } std::size_t get_arguments_length() const { return std::accumulate(std::begin(mNames), std::end(mNames), std::size_t(0), [](const auto &sum, const auto &s) { return sum + s.size() + 1; // +1 for space between names }); } friend std::ostream &operator<<(std::ostream &stream, const Argument &argument) { std::stringstream nameStream; std::copy(std::begin(argument.mNames), std::end(argument.mNames), std::ostream_iterator(nameStream, " ")); stream << nameStream.str() << "\t" << argument.mHelp; if (argument.mDefaultValue.has_value()) { if (!argument.mHelp.empty()) stream << " "; stream << "[default: " << argument.mDefaultValueRepr << "]"; } else if (argument.mIsRequired) { if (!argument.mHelp.empty()) stream << " "; stream << "[required]"; } stream << "\n"; return stream; } template bool operator!=(const T &aRhs) const { return !(*this == aRhs); } /* * Compare to an argument value of known type * @throws std::logic_error in case of incompatible types */ template bool operator==(const T &aRhs) const { if constexpr (!details::is_container_v) { return get() == aRhs; } else { using ValueType = typename T::value_type; auto tLhs = get(); return std::equal(std::begin(tLhs), std::end(tLhs), std::begin(aRhs), std::end(aRhs), [](const auto &lhs, const auto &rhs) { return std::any_cast(lhs) == rhs; }); } } private: static constexpr int eof = std::char_traits::eof(); static auto lookahead(std::string_view s) -> int { if (s.empty()) return eof; else return static_cast(static_cast(s[0])); } /* * decimal-literal: * '0' * nonzero-digit digit-sequence_opt * integer-part fractional-part * fractional-part * integer-part '.' exponent-part_opt * integer-part exponent-part * * integer-part: * digit-sequence * * fractional-part: * '.' post-decimal-point * * post-decimal-point: * digit-sequence exponent-part_opt * * exponent-part: * 'e' post-e * 'E' post-e * * post-e: * sign_opt digit-sequence * * sign: one of * '+' '-' */ static bool is_decimal_literal(std::string_view s) { auto is_digit = [](auto c) constexpr { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return true; default: return false; } }; // precondition: we have consumed or will consume at least one digit auto consume_digits = [=](std::string_view s) { auto it = std::find_if_not(std::begin(s), std::end(s), is_digit); return s.substr(it - std::begin(s)); }; switch (lookahead(s)) { case '0': { s.remove_prefix(1); if (s.empty()) return true; else goto integer_part; } case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { s = consume_digits(s); if (s.empty()) return true; else goto integer_part_consumed; } case '.': { s.remove_prefix(1); goto post_decimal_point; } default: return false; } integer_part: s = consume_digits(s); integer_part_consumed: switch (lookahead(s)) { case '.': { s.remove_prefix(1); if (is_digit(lookahead(s))) goto post_decimal_point; else goto exponent_part_opt; } case 'e': case 'E': { s.remove_prefix(1); goto post_e; } default: return false; } post_decimal_point: if (is_digit(lookahead(s))) { s = consume_digits(s); goto exponent_part_opt; } else { return false; } exponent_part_opt: switch (lookahead(s)) { case eof: return true; case 'e': case 'E': { s.remove_prefix(1); goto post_e; } default: return false; } post_e: switch (lookahead(s)) { case '-': case '+': s.remove_prefix(1); } if (is_digit(lookahead(s))) { s = consume_digits(s); return s.empty(); } else { return false; } } static bool is_optional(std::string_view aName) { return !is_positional(aName); } /* * positional: * _empty_ * '-' * '-' decimal-literal * !'-' anything */ static bool is_positional(std::string_view aName) { switch (lookahead(aName)) { case eof: return true; case '-': { aName.remove_prefix(1); if (aName.empty()) return true; else return is_decimal_literal(aName); } default: return true; } } /* * Get argument value given a type * @throws std::logic_error in case of incompatible types */ template T get() const { if (!mValues.empty()) { if constexpr (details::is_container_v) return any_cast_container(mValues); else return std::any_cast(mValues.front()); } if (mDefaultValue.has_value()) { return std::any_cast(mDefaultValue); } throw std::logic_error("No value provided for '" + mNames.back() + "'."); } /* * Get argument value given a type. * @pre The object has no default value. * @returns The stored value if any, std::nullopt otherwise. */ template auto present() const -> std::optional { if (mDefaultValue.has_value()) throw std::logic_error("Argument with default value always presents"); if (mValues.empty()) return std::nullopt; else if constexpr (details::is_container_v) return any_cast_container(mValues); else return std::any_cast(mValues.front()); } template static auto any_cast_container(const std::vector &aOperand) -> T { using ValueType = typename T::value_type; T tResult; std::transform( std::begin(aOperand), std::end(aOperand), std::back_inserter(tResult), [](const auto &value) { return std::any_cast(value); }); return tResult; } std::vector mNames; std::string_view mUsedName; std::string mHelp; std::any mDefaultValue; std::string mDefaultValueRepr; std::any mImplicitValue; using valued_action = std::function; using void_action = std::function; std::variant mAction{ std::in_place_type, [](const std::string &aValue) { return aValue; }}; std::vector mValues; int mNumArgs = 1; bool mIsOptional : true; bool mIsRequired : true; bool mIsRepeatable : true; bool mIsUsed : true; // True if the optional argument is used by user }; class ArgumentParser { public: explicit ArgumentParser(std::string aProgramName = {}, std::string aVersion = "1.0", default_arguments aArgs = default_arguments::all) : mProgramName(std::move(aProgramName)), mVersion(std::move(aVersion)) { if (aArgs & default_arguments::help) { add_argument("-h", "--help") .action([&](const auto &) { std::cout << help().str(); std::exit(0); }) .default_value(false) .help("shows help message and exits") .implicit_value(true) .nargs(0); } if (aArgs & default_arguments::version) { add_argument("-v", "--version") .action([&](const auto &) { std::cout << mVersion; std::exit(0); }) .default_value(false) .help("prints version information and exits") .implicit_value(true) .nargs(0); } } ArgumentParser(ArgumentParser &&) noexcept = default; ArgumentParser &operator=(ArgumentParser &&) = default; ArgumentParser(const ArgumentParser &other) : mProgramName(other.mProgramName), mVersion(other.mVersion), mDescription(other.mDescription), mEpilog(other.mEpilog), mIsParsed(other.mIsParsed), mPositionalArguments(other.mPositionalArguments), mOptionalArguments(other.mOptionalArguments) { for (auto it = std::begin(mPositionalArguments); it != std::end(mPositionalArguments); ++it) index_argument(it); for (auto it = std::begin(mOptionalArguments); it != std::end(mOptionalArguments); ++it) index_argument(it); } ArgumentParser &operator=(const ArgumentParser &other) { auto tmp = other; std::swap(*this, tmp); return *this; } // Parameter packing // Call add_argument with variadic number of string arguments template Argument &add_argument(Targs... Fargs) { using array_of_sv = std::string_view[sizeof...(Targs)]; auto tArgument = mOptionalArguments.emplace(cend(mOptionalArguments), array_of_sv{Fargs...}); if (!tArgument->mIsOptional) mPositionalArguments.splice(cend(mPositionalArguments), mOptionalArguments, tArgument); index_argument(tArgument); return *tArgument; } // Parameter packed add_parents method // Accepts a variadic number of ArgumentParser objects template ArgumentParser &add_parents(const Targs &... Fargs) { for (const ArgumentParser &tParentParser : {std::ref(Fargs)...}) { for (auto &tArgument : tParentParser.mPositionalArguments) { auto it = mPositionalArguments.insert(cend(mPositionalArguments), tArgument); index_argument(it); } for (auto &tArgument : tParentParser.mOptionalArguments) { auto it = mOptionalArguments.insert(cend(mOptionalArguments), tArgument); index_argument(it); } } return *this; } ArgumentParser &add_description(std::string aDescription) { mDescription = std::move(aDescription); return *this; } ArgumentParser &add_epilog(std::string aEpilog) { mEpilog = std::move(aEpilog); return *this; } /* Call parse_args_internal - which does all the work * Then, validate the parsed arguments * This variant is used mainly for testing * @throws std::runtime_error in case of any invalid argument */ void parse_args(const std::vector &aArguments) { parse_args_internal(aArguments); parse_args_validate(); } /* Main entry point for parsing command-line arguments using this * ArgumentParser * @throws std::runtime_error in case of any invalid argument */ void parse_args(int argc, const char *const argv[]) { std::vector arguments; std::copy(argv, argv + argc, std::back_inserter(arguments)); parse_args(arguments); } /* Getter for options with default values. * @throws std::logic_error if parse_args() has not been previously called * @throws std::logic_error if there is no such option * @throws std::logic_error if the option has no value * @throws std::bad_any_cast if the option is not of type T */ template T get(std::string_view aArgumentName) const { if (!mIsParsed) { throw std::logic_error("Nothing parsed, no arguments are available."); } return (*this)[aArgumentName].get(); } /* Getter for options without default values. * @pre The option has no default value. * @throws std::logic_error if there is no such option * @throws std::bad_any_cast if the option is not of type T */ template auto present(std::string_view aArgumentName) const -> std::optional { return (*this)[aArgumentName].present(); } /* Getter that returns true for user-supplied options. Returns false if not * user-supplied, even with a default value. */ auto is_used(std::string_view aArgumentName) const { return (*this)[aArgumentName].mIsUsed; } /* Indexing operator. Return a reference to an Argument object * Used in conjuction with Argument.operator== e.g., parser["foo"] == true * @throws std::logic_error in case of an invalid argument name */ Argument &operator[](std::string_view aArgumentName) const { auto tIterator = mArgumentMap.find(aArgumentName); if (tIterator != mArgumentMap.end()) { return *(tIterator->second); } if (aArgumentName.front() != '-') { std::string nameStr(aArgumentName); // "-" + aArgumentName nameStr = "-" + nameStr; tIterator = mArgumentMap.find(nameStr); if (tIterator != mArgumentMap.end()) { return *(tIterator->second); } // "--" + aArgumentName nameStr = "-" + nameStr; tIterator = mArgumentMap.find(nameStr); if (tIterator != mArgumentMap.end()) { return *(tIterator->second); } } throw std::logic_error("No such argument: " + std::string(aArgumentName)); } // Print help message friend auto operator<<(std::ostream &stream, const ArgumentParser &parser) -> std::ostream & { stream.setf(std::ios_base::left); stream << "Usage: " << parser.mProgramName << " [options] "; std::size_t tLongestArgumentLength = parser.get_length_of_longest_argument(); for (const auto &argument : parser.mPositionalArguments) { stream << argument.mNames.front() << " "; } stream << "\n\n"; if (!parser.mDescription.empty()) stream << parser.mDescription << "\n\n"; if (!parser.mPositionalArguments.empty()) stream << "Positional arguments:\n"; for (const auto &mPositionalArgument : parser.mPositionalArguments) { stream.width(tLongestArgumentLength); stream << mPositionalArgument; } if (!parser.mOptionalArguments.empty()) stream << (parser.mPositionalArguments.empty() ? "" : "\n") << "Optional arguments:\n"; for (const auto &mOptionalArgument : parser.mOptionalArguments) { stream.width(tLongestArgumentLength); stream << mOptionalArgument; } if (!parser.mEpilog.empty()) stream << parser.mEpilog << "\n\n"; return stream; } // Format help message auto help() const -> std::stringstream { std::stringstream out; out << *this; return out; } // Printing the one and only help message // I've stuck with a simple message format, nothing fancy. [[deprecated("Use cout << program; instead. See also help().")]] std::string print_help() const { auto out = help(); std::cout << out.rdbuf(); return out.str(); } private: /* * @throws std::runtime_error in case of any invalid argument */ void parse_args_internal(const std::vector &aArguments) { if (mProgramName.empty() && !aArguments.empty()) { mProgramName = aArguments.front(); } auto end = std::end(aArguments); auto positionalArgumentIt = std::begin(mPositionalArguments); for (auto it = std::next(std::begin(aArguments)); it != end;) { const auto &tCurrentArgument = *it; if (Argument::is_positional(tCurrentArgument)) { if (positionalArgumentIt == std::end(mPositionalArguments)) { throw std::runtime_error( "Maximum number of positional arguments exceeded"); } auto tArgument = positionalArgumentIt++; it = tArgument->consume(it, end); continue; } auto tIterator = mArgumentMap.find(tCurrentArgument); if (tIterator != mArgumentMap.end()) { auto tArgument = tIterator->second; it = tArgument->consume(std::next(it), end, tIterator->first); } else if (const auto &tCompoundArgument = tCurrentArgument; tCompoundArgument.size() > 1 && tCompoundArgument[0] == '-' && tCompoundArgument[1] != '-') { ++it; for (std::size_t j = 1; j < tCompoundArgument.size(); j++) { auto tHypotheticalArgument = std::string{'-', tCompoundArgument[j]}; auto tIterator2 = mArgumentMap.find(tHypotheticalArgument); if (tIterator2 != mArgumentMap.end()) { auto tArgument = tIterator2->second; it = tArgument->consume(it, end, tIterator2->first); } else { throw std::runtime_error("Unknown argument: " + tCurrentArgument); } } } else { throw std::runtime_error("Unknown argument: " + tCurrentArgument); } } mIsParsed = true; } /* * @throws std::runtime_error in case of any invalid argument */ void parse_args_validate() { // Check if all arguments are parsed std::for_each(std::begin(mArgumentMap), std::end(mArgumentMap), [](const auto &argPair) { const auto &tArgument = argPair.second; tArgument->validate(); }); } // Used by print_help. std::size_t get_length_of_longest_argument() const { if (mArgumentMap.empty()) return 0; std::vector argumentLengths(mArgumentMap.size()); std::transform(std::begin(mArgumentMap), std::end(mArgumentMap), std::begin(argumentLengths), [](const auto &argPair) { const auto &tArgument = argPair.second; return tArgument->get_arguments_length(); }); return *std::max_element(std::begin(argumentLengths), std::end(argumentLengths)); } using list_iterator = std::list::iterator; void index_argument(list_iterator argIt) { for (auto &mName : std::as_const(argIt->mNames)) mArgumentMap.insert_or_assign(mName, argIt); } std::string mProgramName; std::string mVersion; std::string mDescription; std::string mEpilog; bool mIsParsed = false; std::list mPositionalArguments; std::list mOptionalArguments; std::map> mArgumentMap; }; } // namespace argparse ================================================ FILE: src/libs/util/include/util/enum.hpp ================================================ #pragma once namespace Megaverse { enum class Status { SUCCESS, ERROR, }; } ================================================ FILE: src/libs/util/include/util/filesystem_utils.hpp ================================================ #pragma once #include #include #include #include namespace Megaverse { inline char pathDelim() { #if defined(_WIN32) return '\\'; #else return '/'; #endif } template void pathJoinHelper(std::ostringstream &stream, const T &t) { stream << t; } template void pathJoinHelper(std::ostringstream &stream, const T &t, Args... args) { stream << t << pathDelim(); pathJoinHelper(stream, std::forward(args)...); } template std::string pathJoin(Args... args) { std::ostringstream stream; pathJoinHelper(stream, std::forward(args)...); return stream.str(); } /// Returns number of bytes read. size_t readAllBytes(const std::string &filename, std::vector &buffer); size_t readAllBytes(std::ifstream &stream, std::vector &buffer); /// Actually checks if file is accessible. bool fileExists(const std::string &filename); std::vector listFilesInDirectory(const std::string &dir); } ================================================ FILE: src/libs/util/include/util/macro.hpp ================================================ #pragma once // compiler-dependent attributes #if defined(__clang__) || defined(__GNUG__) #define FORCE_INLINE inline __attribute__((always_inline)) #else #define FORCE_INLINE __forceinline #endif #define EPSILON 1e-5f #define ARR_LENGTH(arr) sizeof(arr) / sizeof(arr[0]) #define UNUSED(x) (void)(x) ================================================ FILE: src/libs/util/include/util/magnum.hpp ================================================ #pragma once #include #include #include #include namespace Megaverse { using Object3D = Magnum::SceneGraph::Object; using Scene3D = Magnum::SceneGraph::Scene; using Radians = Magnum::Math::Rad; template std::ostream &operator<<(std::ostream &stream, const Magnum::Math::Vector3 &v) { stream << v.x() << " " << v.y() << " " << v.z(); return stream; } inline Magnum::Color3 toRgbf(unsigned long long value) { return Magnum::Math::unpack( Magnum::Math::Color3{ Magnum::UnsignedByte(value >> 16), Magnum::UnsignedByte(value >> 8), Magnum::UnsignedByte(value) } ); } inline Magnum::Math::Deg degrees(double value) { return Magnum::Math::Deg(Magnum::Float(value)); } } namespace Magnum::Math { template inline Vector lround(const Vector& a) { Vector out{Magnum::NoInit}; for(std::size_t i = 0; i != size; ++i) out[i] = std::lround(a[i]); return out; } template std::vector toFloat(const CONTAINER_T &vectors) { std::vector result; result.reserve(vectors.size()); for (const auto &v : vectors) result.emplace_back(v); return result; } } ================================================ FILE: src/libs/util/include/util/math_utils.hpp ================================================ #pragma once namespace Megaverse { template T triangularNumber(T n) { return n * (n + 1) / 2; } } ================================================ FILE: src/libs/util/include/util/os_utils.hpp ================================================ #include #include #include /** * https://gist.github.com/thirdwing/da4621eb163a886a03c5 * @param vm_usage Virtual memory size in bytes * @param resident_set Resident Set Size: number of pages the process has in real memory (in bytes) */ inline void unixProcessMemUsage(double& vm_usage, double& resident_set) { vm_usage = 0.0; resident_set = 0.0; // the two fields we want unsigned long vsize; long rss; { std::string ignore; std::ifstream ifs("/proc/self/stat", std::ios_base::in); ifs >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> vsize >> rss; } static long page_size_bytes = sysconf(_SC_PAGE_SIZE); // in case x86-64 is configured to use 2MB pages vm_usage = double(vsize); resident_set = double(rss) * double(page_size_bytes); } ================================================ FILE: src/libs/util/include/util/perlin_noise.hpp ================================================ //---------------------------------------------------------------------------------------- // // siv::PerlinNoise // Perlin noise library for modern C++ // // Copyright (C) 2013-2020 Ryo Suzuki // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // //---------------------------------------------------------------------------------------- # pragma once # include # include # include # ifdef __cpp_concepts # if __has_include() # include # endif # endif # include # include # include # include namespace siv { # ifdef __cpp_lib_concepts template # else template # endif class BasicPerlinNoise { public: using value_type = Float; private: std::uint8_t p[512]; [[nodiscard]] static constexpr value_type Fade(value_type t) noexcept { return t * t * t * (t * (t * 6 - 15) + 10); } [[nodiscard]] static constexpr value_type Lerp(value_type t, value_type a, value_type b) noexcept { return a + t * (b - a); } [[nodiscard]] static constexpr value_type Grad(std::uint8_t hash, value_type x, value_type y, value_type z) noexcept { const std::uint8_t h = hash & 15; const value_type u = h < 8 ? x : y; const value_type v = h < 4 ? y : h == 12 || h == 14 ? x : z; return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); } [[nodiscard]] static constexpr value_type Weight(std::int32_t octaves) noexcept { value_type amp = 1; value_type value = 0; for (std::int32_t i = 0; i < octaves; ++i) { value += amp; amp /= 2; } return value; } public: # if __has_cpp_attribute(nodiscard) >= 201907L [[nodiscard]] # endif explicit BasicPerlinNoise(std::uint32_t seed = std::default_random_engine::default_seed) { reseed(seed); } # ifdef __cpp_lib_concepts template # else template >* = nullptr> # endif # if __has_cpp_attribute(nodiscard) >= 201907L [[nodiscard]] # endif explicit BasicPerlinNoise(URNG&& urng) { reseed(std::forward(urng)); } void reseed(std::uint32_t seed) { for (size_t i = 0; i < 256; ++i) { p[i] = static_cast(i); } std::shuffle(std::begin(p), std::begin(p) + 256, std::default_random_engine(seed)); for (size_t i = 0; i < 256; ++i) { p[256 + i] = p[i]; } } # ifdef __cpp_lib_concepts template # else template >* = nullptr> # endif void reseed(URNG&& urng) { for (size_t i = 0; i < 256; ++i) { p[i] = static_cast(i); } std::shuffle(std::begin(p), std::begin(p) + 256, std::forward(urng)); for (size_t i = 0; i < 256; ++i) { p[256 + i] = p[i]; } } /////////////////////////////////////// // // Noise [-1, 1] // [[nodiscard]] value_type noise1D(value_type x) const noexcept { return noise3D(x, 0, 0); } [[nodiscard]] value_type noise2D(value_type x, value_type y) const noexcept { return noise3D(x, y, 0); } [[nodiscard]] value_type noise3D(value_type x, value_type y, value_type z) const noexcept { const std::int32_t X = static_cast(std::floor(x)) & 255; const std::int32_t Y = static_cast(std::floor(y)) & 255; const std::int32_t Z = static_cast(std::floor(z)) & 255; x -= std::floor(x); y -= std::floor(y); z -= std::floor(z); const value_type u = Fade(x); const value_type v = Fade(y); const value_type w = Fade(z); const std::int32_t A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z; const std::int32_t B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z; return Lerp(w, Lerp(v, Lerp(u, Grad(p[AA], x, y, z), Grad(p[BA], x - 1, y, z)), Lerp(u, Grad(p[AB], x, y - 1, z), Grad(p[BB], x - 1, y - 1, z))), Lerp(v, Lerp(u, Grad(p[AA + 1], x, y, z - 1), Grad(p[BA + 1], x - 1, y, z - 1)), Lerp(u, Grad(p[AB + 1], x, y - 1, z - 1), Grad(p[BB + 1], x - 1, y - 1, z - 1)))); } /////////////////////////////////////// // // Noise [0, 1] // [[nodiscard]] value_type noise1D_0_1(value_type x) const noexcept { return noise1D(x) * value_type(0.5) + value_type(0.5); } [[nodiscard]] value_type noise2D_0_1(value_type x, value_type y) const noexcept { return noise2D(x, y) * value_type(0.5) + value_type(0.5); } [[nodiscard]] value_type noise3D_0_1(value_type x, value_type y, value_type z) const noexcept { return noise3D(x, y, z) * value_type(0.5) + value_type(0.5); } /////////////////////////////////////// // // Accumulated octave noise // * Return value can be outside the range [-1, 1] // [[nodiscard]] value_type accumulatedOctaveNoise1D(value_type x, std::int32_t octaves) const noexcept { value_type result = 0; value_type amp = 1; for (std::int32_t i = 0; i < octaves; ++i) { result += noise1D(x) * amp; x *= 2; amp /= 2; } return result; // unnormalized } [[nodiscard]] value_type accumulatedOctaveNoise2D(value_type x, value_type y, std::int32_t octaves) const noexcept { value_type result = 0; value_type amp = 1; for (std::int32_t i = 0; i < octaves; ++i) { result += noise2D(x, y) * amp; x *= 2; y *= 2; amp /= 2; } return result; // unnormalized } [[nodiscard]] value_type accumulatedOctaveNoise3D(value_type x, value_type y, value_type z, std::int32_t octaves) const noexcept { value_type result = 0; value_type amp = 1; for (std::int32_t i = 0; i < octaves; ++i) { result += noise3D(x, y, z) * amp; x *= 2; y *= 2; z *= 2; amp /= 2; } return result; // unnormalized } /////////////////////////////////////// // // Normalized octave noise [-1, 1] // [[nodiscard]] value_type normalizedOctaveNoise1D(value_type x, std::int32_t octaves) const noexcept { return accumulatedOctaveNoise1D(x, octaves) / Weight(octaves); } [[nodiscard]] value_type normalizedOctaveNoise2D(value_type x, value_type y, std::int32_t octaves) const noexcept { return accumulatedOctaveNoise2D(x, y, octaves) / Weight(octaves); } [[nodiscard]] value_type normalizedOctaveNoise3D(value_type x, value_type y, value_type z, std::int32_t octaves) const noexcept { return accumulatedOctaveNoise3D(x, y, z, octaves) / Weight(octaves); } /////////////////////////////////////// // // Accumulated octave noise clamped within the range [0, 1] // [[nodiscard]] value_type accumulatedOctaveNoise1D_0_1(value_type x, std::int32_t octaves) const noexcept { return std::clamp(accumulatedOctaveNoise1D(x, octaves) * value_type(0.5) + value_type(0.5), 0, 1); } [[nodiscard]] value_type accumulatedOctaveNoise2D_0_1(value_type x, value_type y, std::int32_t octaves) const noexcept { return std::clamp(accumulatedOctaveNoise2D(x, y, octaves) * value_type(0.5) + value_type(0.5), 0, 1); } [[nodiscard]] value_type accumulatedOctaveNoise3D_0_1(value_type x, value_type y, value_type z, std::int32_t octaves) const noexcept { return std::clamp(accumulatedOctaveNoise3D(x, y, z, octaves) * value_type(0.5) + value_type(0.5), 0, 1); } /////////////////////////////////////// // // Normalized octave noise [0, 1] // [[nodiscard]] value_type normalizedOctaveNoise1D_0_1(value_type x, std::int32_t octaves) const noexcept { return normalizedOctaveNoise1D(x, octaves) * value_type(0.5) + value_type(0.5); } [[nodiscard]] value_type normalizedOctaveNoise2D_0_1(value_type x, value_type y, std::int32_t octaves) const noexcept { return normalizedOctaveNoise2D(x, y, octaves) * value_type(0.5) + value_type(0.5); } [[nodiscard]] value_type normalizedOctaveNoise3D_0_1(value_type x, value_type y, value_type z, std::int32_t octaves) const noexcept { return normalizedOctaveNoise3D(x, y, z, octaves) * value_type(0.5) + value_type(0.5); } /////////////////////////////////////// // // Serialization // void serialize(std::array& s) const noexcept { for (std::size_t i = 0; i < 256; ++i) { s[i] = p[i]; } } void deserialize(const std::array& s) noexcept { for (std::size_t i = 0; i < 256; ++i) { p[256 + i] = p[i] = s[i]; } } /////////////////////////////////////// // // Legacy interface // [[deprecated("use noise1D() instead")]] double noise(double x) const; [[deprecated("use noise2D() instead")]] double noise(double x, double y) const; [[deprecated("use noise3D() instead")]] double noise(double x, double y, double z) const; [[deprecated("use noise1D_0_1() instead")]] double noise0_1(double x) const; [[deprecated("use noise2D_0_1() instead")]] double noise0_1(double x, double y) const; [[deprecated("use noise3D_0_1() instead")]] double noise0_1(double x, double y, double z) const; [[deprecated("use accumulatedOctaveNoise1D() instead")]] double octaveNoise(double x, std::int32_t octaves) const; [[deprecated("use accumulatedOctaveNoise2D() instead")]] double octaveNoise(double x, double y, std::int32_t octaves) const; [[deprecated("use accumulatedOctaveNoise3D() instead")]] double octaveNoise(double x, double y, double z, std::int32_t octaves) const; [[deprecated("use accumulatedOctaveNoise1D_0_1() instead")]] double octaveNoise0_1(double x, std::int32_t octaves) const; [[deprecated("use accumulatedOctaveNoise2D_0_1() instead")]] double octaveNoise0_1(double x, double y, std::int32_t octaves) const; [[deprecated("use accumulatedOctaveNoise3D_0_1() instead")]] double octaveNoise0_1(double x, double y, double z, std::int32_t octaves) const; }; using PerlinNoise = BasicPerlinNoise; } ================================================ FILE: src/libs/util/include/util/string_utils.hpp ================================================ #pragma once #include #include #include namespace Megaverse { std::vector splitString(const std::string &s, const std::string &d); bool startsWith(const std::string &s, const std::string &t); bool endsWith(const std::string &s, const std::string &t); std::string toLower(const std::string &s); template T stringTo(const std::string &s, bool &ok) { T val; // slow but easy to write if (std::istringstream(s) >> val) { ok = true; return val; } ok = false; return T(); } } ================================================ FILE: src/libs/util/include/util/tiny_logger.hpp ================================================ #pragma once #include #include #include #include #include namespace Megaverse { enum LogLevel { FATAL, ERROR, WARNING, INFO, VERBOSE, DEBUG, }; void setLogLevel(LogLevel level); class LogMessage { public: LogMessage(LogLevel level, const char *file, int line, const char *func, std::ostream *outStream = &std::cout); ~LogMessage(); std::ostringstream &operator()(); private: std::shared_ptr stream; std::ostream *outStream; LogLevel level; }; class NullStream : public std::ostringstream { public: NullStream() : std::ostringstream() {} NullStream &operator()() { return *this; } }; template inline NullStream &operator<<(NullStream &stream, T &&) { return stream; } // helper macros #define TLOG(level) LogMessage(level, __FILE__, __LINE__, __FUNCTION__)() #define TLOG_IF(level, cond) !(cond) ? NullStream()() : TLOG(level) // die if condition is not true #define TCHECK(cond) TLOG_IF(FATAL, !(cond)) // various helper functions template std::ostream &operator<<(std::ostream &stream, const std::vector &vec) { stream << '['; const auto end = std::end(vec); for (auto it = std::begin(vec); it != end; ++it) { stream << *it; if (it + 1 != end) stream << ", "; } stream << ']'; return stream; } } ================================================ FILE: src/libs/util/include/util/tiny_profiler.hpp ================================================ #pragma once #include namespace Megaverse { class TinyProfiler { /// Private implementation to hide some stl headers. struct TinyProfilerImpl; public: /// Singleton. static TinyProfiler &instance(); /// Start measurement for key. void startTimer(const std::string &key); /// Stop time measurement, but keep time measured so far. void pauseTimer(const std::string &key); /// Read total measured time since start. float readTimer(const std::string &key, bool log = false); /// Stop measurement for key, returns all measured time since start. float stopTimer(const std::string &key, bool log = false); TinyProfiler(const TinyProfiler &) = delete; void operator=(const TinyProfiler &) = delete; private: TinyProfiler(); private: std::unique_ptr data; }; TinyProfiler &tprof(); } ================================================ FILE: src/libs/util/include/util/util.hpp ================================================ #pragma once #include #include #include #include namespace Megaverse { template int sgn(T val) { return (T(0) < val) - (val < T(0)); } template FORCE_INLINE T sqr(T x) { return x * x; } using Rng = std::mt19937; /** * @return random integer from [low, high). */ inline int randRange(int low, int high, Rng &rng) { return std::uniform_int_distribution<>{low, high - 1}(rng); } /** * @return random boolean. */ inline bool randomBool(Rng &rng) { return bool(randRange(0, 2, rng)); } /** * @return random number in [0, 1) */ inline float frand(Rng &rng) { return std::uniform_real_distribution{0, 1}(rng); } template auto randomSample(const CONTAINER_T &container, Rng &rng) { const auto idx = randRange(0, container.size(), rng); return container[idx]; } template void endianSwap(T *x) { int size = sizeof(*x); std::reverse(static_cast(x), static_cast(x + size)); } template bool contains(const C &c, const T &val) { return std::find(std::begin(c), std::end(c), val) != std::end(c); } void memcpyStride(char *dst, const char *src, int elemSize, int numElements, int ofs, int stride); } ================================================ FILE: src/libs/util/include/util/voxel_grid.hpp ================================================ #pragma once #include #include #include #include namespace Megaverse { using VoxelCoords = Magnum::Vector3i; constexpr int maxGridResolution = 1'024, logMaxGridResolution = 10, maxAbsVoxelCoord = maxGridResolution / 2; inline VoxelCoords toVoxel(const Magnum::Vector3 &v) { return lround(floor(v)); } inline int manhattanDistance(const VoxelCoords &a, const VoxelCoords &b) { const auto delta = a - b; return std::abs(delta.x()) + std::abs(delta.y()) + std::abs(delta.z()); } } //bool operator==(const VoxelCoords &lhs, const VoxelCoords &rhs) //{ // return lhs.x() == rhs.x() && lhs.y() == rhs.y() && lhs.z() == rhs.z(); //} namespace std { template <> struct hash { std::size_t operator()(const Megaverse::VoxelCoords &voxel) const noexcept { constexpr auto shift = Megaverse::logMaxGridResolution; constexpr auto offset = Megaverse::maxAbsVoxelCoord; const int x = voxel.x() + offset, y = voxel.y() + offset, z = voxel.z() + offset; return size_t((x << (2 * shift)) + (y << shift) + z); } }; } namespace Megaverse { template class VoxelGrid { public: using HashMap = std::unordered_map; public: /** * Ctor. * @param voxelCount number of voxels to reserve initially. * @param origin point with the lowest coords covered by the voxelGrid. Corresponds to voxel {0, 0, 0}. * @param voxelSize scale of one voxel */ explicit VoxelGrid(size_t voxelCount, const Magnum::Vector3 &origin, float voxelSize) : voxelCount{voxelCount} , grid{voxelCount} , origin{origin} , voxelSize{voxelSize} {} /** * Reset the grid (empty). */ void clear() { grid = HashMap{voxelCount}; } /** * @param coords coordinates of a voxel. * @return check whether voxel at these coordinates is present in the map. */ bool hasVoxel(const VoxelCoords &coords) const { return bool(grid.count(coords)); } /** * @param coords location in voxel grid. * @return pointer to VoxelState at the "coords" location, or nullptr if nothing is there. */ const VoxelState * get(const VoxelCoords &coords) const { auto voxelIt = grid.find(coords); if (voxelIt == grid.end()) return nullptr; return &(voxelIt->second); } VoxelState * get(const VoxelCoords &coords) { auto voxelIt = grid.find(coords); if (voxelIt == grid.end()) return nullptr; return &(voxelIt->second); } const VoxelState * getWithVector(const Magnum::Vector3 &v) const { return get(getCoords(v)); } VoxelState * getWithVector(const Magnum::Vector3 &v) { return get(getCoords(v)); } /** * Override the voxel state in the particular voxel. * @param coords * @param state */ void set(const VoxelCoords &coords, const VoxelState &state) { grid[coords] = state; } void remove(const VoxelCoords &coords) { grid.erase(coords); } /** * Convert floating point coordinates of a point in space to voxel coordinates. * @param v point (vector) in 3D space. * @return corresponding voxel coords. */ VoxelCoords getCoords(const Magnum::Vector3 &v) const { const auto coordsFloat = (v - origin) / voxelSize; const auto coords = toVoxel(coordsFloat); return coords; } const HashMap & getHashMap() const { return grid; } float getVoxelSize() const { return voxelSize; } private: size_t voxelCount; HashMap grid; Magnum::Vector3 origin; float voxelSize; }; } ================================================ FILE: src/libs/util/src/filesystem_utils.cpp ================================================ #include namespace Megaverse { size_t readAllBytes(const std::string &filename, std::vector &buffer) { std::ifstream f{filename, std::ios::in | std::ios::binary}; return readAllBytes(f, buffer); } size_t readAllBytes(std::ifstream &stream, std::vector &buffer) { stream.seekg(0, std::ios::end); const auto size = stream.tellg(); stream.seekg(0); buffer.resize(size); if (stream.read(buffer.data(), size)) return size; else return 0; } bool fileExists(const std::string& filename) { std::ifstream f(filename); return f.good(); } // This crashes on GCC 8.4 due to some obscure linking error (let's just wait for a new compiler I guess lol) //std::vector listFilesInDirectory(const std::string &dir) //{ // std::vector result; // // for (const auto & entry : fs::directory_iterator(dir)) // result.emplace_back(entry.path().string()); // // return result; //} } ================================================ FILE: src/libs/util/src/string_utils.cpp ================================================ #include #include #include namespace Megaverse { std::vector splitString(const std::string &s, const std::string &d) { std::vector result; char *cStr = new char[s.size() + 1]; strcpy(cStr, s.c_str()); char *strtokPtr = nullptr; char *token = strtok_r(cStr, d.c_str(), &strtokPtr); while (token) { result.emplace_back(token); token = strtok_r(nullptr, d.c_str(), &strtokPtr); } delete[] cStr; return result; } bool startsWith(const std::string &s, const std::string &t) { if (t.empty()) return true; return s.find(t) == 0; } bool endsWith(const std::string &s, const std::string &t) { if (t.size() > s.size()) return false; if (t.empty()) return true; return s.find(t) == s.size() - t.size(); } std::string toLower(const std::string &input) { auto s = input; std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return std::tolower(c); }); return s; } } ================================================ FILE: src/libs/util/src/tiny_logger.cpp ================================================ #include #include #include #include #include #include using namespace std::chrono; using namespace std::chrono_literals; using namespace Megaverse; namespace { /// Everything with level higher than logLevel will be discarded. LogLevel logLevel = DEBUG; const char *levelToStr(LogLevel level) { switch (level) { case FATAL: return "FAT"; case ERROR: return "ERR"; case WARNING: return "WRN"; case INFO: return "INF"; case VERBOSE: return "VER"; case DEBUG: return "DBG"; default: return ""; } } const char *constBasename(const char *filepath) { const char *basename = strrchr(filepath, pathDelim()); return basename ? basename + 1 : filepath; } void printTime(std::ostream &stream) { char timeStr[1 << 6]; const system_clock::time_point now = system_clock::now(); const time_t timestamp = system_clock::to_time_t(now); strftime(timeStr, sizeof(timeStr), "%m-%d %T", std::localtime(×tamp)); const auto sinceEpoch = now.time_since_epoch(); const system_clock::duration fraction = sinceEpoch - duration_cast(sinceEpoch); const long long ms = duration_cast(fraction).count(); stream << timeStr << '.' << std::setw(3) << std::setfill('0') << ms << ' '; } } namespace Megaverse { void setLogLevel(LogLevel level) { logLevel = level; } LogMessage::LogMessage(LogLevel lvl, const char *file, int line, const char *func, std::ostream *outStream) : stream{nullptr} , outStream{outStream} , level{lvl} { if (level > logLevel) { static std::shared_ptr nullStream = std::make_shared(); stream = nullStream; } else { stream = std::make_shared(); *stream << "["; printTime(*stream); *stream << levelToStr(level) << ' ' << constBasename(file) << ':' << line; if (func) { *stream << ' ' << func; if (!strchr(func, '(')) *stream << "()"; } *stream << "] "; } } LogMessage::~LogMessage() { if (stream) *stream << '\n'; if (level <= logLevel) { static std::mutex mutex; std::lock_guard lock(mutex); *outStream << stream->str(); } if (level == FATAL) { *outStream << "\nExiting due to FATAL error, see the logs for details...\n\n\n"; std::this_thread::sleep_for(2s); exit(-1); } } std::ostringstream &LogMessage::operator()() { return *stream; } } ================================================ FILE: src/libs/util/src/tiny_profiler.cpp ================================================ #include #include #include #include #include #include #include using std::chrono::high_resolution_clock; using std::chrono::duration; using std::chrono::duration_cast; using std::chrono::microseconds; using std::chrono::milliseconds; using namespace Megaverse; namespace { // types typedef high_resolution_clock::time_point tstamp; // helper functions inline microseconds::rep passedSince(const tstamp &tstamp) { const auto now = high_resolution_clock::now(); const auto passedUsec = duration_cast(now - tstamp).count(); return passedUsec; } } namespace Megaverse { struct TinyProfiler::TinyProfilerImpl { std::map timestamps; std::map totalTime; }; TinyProfiler & TinyProfiler::instance() { static TinyProfiler profiler; return profiler; } TinyProfiler & tprof() { return TinyProfiler::instance(); } } TinyProfiler::TinyProfiler() { data = std::make_unique(); } void TinyProfiler::startTimer(const std::string &key) { data->timestamps[key] = high_resolution_clock::now(); } void TinyProfiler::pauseTimer(const std::string &key) { if (!data->timestamps.count(key)) { TLOG(ERROR) << "No such timer: " << key; return; } const auto passedUsec = passedSince(data->timestamps[key]); data->totalTime[key] += passedUsec; } float TinyProfiler::readTimer(const std::string &key, bool log) { if (!data->timestamps.count(key)) { TLOG(ERROR) << "No such timer: " << key; return 0.0f; } const auto passedUsec = passedSince(data->timestamps[key]) + data->totalTime[key]; TLOG_IF(INFO, log) << passedUsec << " us passed for " << key; return float(passedUsec); } float TinyProfiler::stopTimer(const std::string &key, bool log) { const auto passedUsec = readTimer(key, log); data->timestamps.erase(key); data->totalTime.erase(key); return passedUsec; } ================================================ FILE: src/libs/util/src/util.cpp ================================================ #include #include namespace Megaverse { void memcpyStride(char *dst, const char *src, int elemSize, int numElements, int ofs, int stride) { for (int i = 0, dstOfs = 0, srcOfs = ofs; i < numElements; ++i, dstOfs += elemSize, srcOfs += stride) memcpy(dst + dstOfs, src + srcOfs, elemSize); } } ================================================ FILE: src/libs/v4r_rendering/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.13) project(libgfx-v4r VERSION 0.1 LANGUAGES CXX) add_library_default(v4r_rendering) target_link_libraries(v4r_rendering util env rendering v4r_headless v4r_debug Magnum::Primitives) ================================================ FILE: src/libs/v4r_rendering/include/v4r_rendering/v4r_env_renderer.hpp ================================================ #pragma once #include #include namespace Megaverse { class V4RDrawable; class V4REnvRenderer : public EnvRenderer { public: /** * @param previousRenderer if renderers are chained (i.e. multiple renderers render the same scene) we need the * pointer to the previous renderer in the chain to provide the list of "dirty" drawables whose absolute * transformations we need to query from the scene graph and update. */ explicit V4REnvRenderer(Envs &envs, int w, int h, V4REnvRenderer *previousRenderer, bool withOverview); ~V4REnvRenderer() override; void reset(Env &env, int envIdx) override; void preDraw(Env &env, int envIndex) override; void draw(Envs &envs) override; const uint8_t * getObservation(int envIdx, int agentIdx) const override; std::vector getDirtyDrawables(int envIdx) const; Overview * getOverview() override; private: struct Impl; std::unique_ptr pimpl; }; } ================================================ FILE: src/libs/v4r_rendering/src/v4r_env_renderer.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace Magnum; using namespace Magnum::Math::Literals; using namespace Megaverse; class Megaverse::V4RDrawable : public SceneGraph::Drawable3D { public: explicit V4RDrawable( SceneGraph::AbstractObject3D &parentObject, v4r::Environment &renderEnv, uint32_t instanceID, SceneGraph::DrawableGroup3D &drawables) : SceneGraph::Drawable3D{parentObject, &drawables}, _renderEnv(renderEnv), _instanceID(instanceID) {} private: void draw(const Matrix4 &transformation, SceneGraph::Camera3D &) override { _renderEnv.updateInstanceTransform(_instanceID, glm::make_mat4(transformation.data())); } public: void updateAbsoluteTransformation() { _renderEnv.updateInstanceTransform(_instanceID, glm::make_mat4(object().absoluteTransformationMatrix().data())); } private: v4r::Environment &_renderEnv; uint32_t _instanceID; }; struct ColorCompare { bool operator()(const Magnum::Color3 &c1, const Magnum::Color3 &c2) const { return c1.r() == c2.r() ? c1.g() == c2.g() ? c1.b() < c2.b() : c1.g() < c2.g() : c1.r() < c2.r(); } }; struct V4REnvRenderer::Impl { public: explicit Impl(Envs &envs, int w, int h, V4REnvRenderer *previousRenderer, bool withOverview); ~Impl(); /** * Reset the state of the renderer between episodes. * @param env */ void reset(Env &env, int envIdx); void preDraw(Env &env, int envIdx); void draw(Envs &envs); const uint8_t * getObservation(int envIdx, int agentIdx) const; /** * Assuming preDraw() and draw() were already called for this renderer before the next renderer in the chain * requests dirty drawables. */ std::vector getDirtyDrawables(int envIdx) const { return dirtyDrawables[envIdx]; } Overview * getOverview() { return &overview; } private: int batchSize(const Envs &envs) const { int res = 0; for (const auto &e : envs) res += e->getNumAgents(); return res; } public: v4r::BatchRenderer renderer; v4r::AssetLoader loader; v4r::CommandStream cmdStream; shared_ptr scene; vector renderEnvs; glm::u32vec2 framebufferSize; int pixelsPerFrame{}, pixelsPerEnv{}; // vector cpuFrames; std::vector envDrawables; std::vector> v4rDrawables; // to avoid dynamic cast on every step() std::vector>> drawablesObjects; std::vector> dirtyDrawables; std::map materialIndices; // v4r::RenderDoc rdoc; std::map meshData; std::map meshIndices; V4REnvRenderer *previousRenderer = nullptr; bool withOverviewCamera = false; Overview overview; }; using Pipeline = v4r::BlinnPhong; V4REnvRenderer::Impl::Impl(Envs &envs, int w, int h, V4REnvRenderer *previousRenderer, bool withOverview) : renderer{{ 0, 1, 1, uint32_t(batchSize(envs)), uint32_t(w), uint32_t(h), glm::mat4(1.f) }, v4r::RenderFeatures {v4r::RenderOptions::CpuSynchronization}} , loader{renderer.makeLoader()} , cmdStream{renderer.makeCommandStream()} , renderEnvs{} , framebufferSize{w, h} , previousRenderer{previousRenderer} , withOverviewCamera{withOverview} // cpuFrames(), // rdoc() { auto numEnvs = envs.size(); envDrawables.resize(numEnvs), drawablesObjects.resize(numEnvs), v4rDrawables.resize(numEnvs), dirtyDrawables.resize(numEnvs); // cpuFrames = vector(size_t(framebufferSize.x * framebufferSize.y * 4 * env.getNumAgents())); vector> meshes; vector> materials; using Vertex = Pipeline::Vertex; using MaterialParams = Pipeline::MaterialParams; // Inefficient conversion auto convertMesh = [&](const Magnum::Trade::MeshData &magnum_mesh) { vector vertices; vector indices; const auto magnum_indices = magnum_mesh.indicesAsArray(); const auto magnum_positions = magnum_mesh.positions3DAsArray(); const auto magnum_normals = magnum_mesh.normalsAsArray(); for (size_t i = 0; i < magnum_positions.size(); i++) { const auto &position = magnum_positions[i]; const auto &normal = magnum_normals[i]; vertices.emplace_back(Vertex { glm::make_vec3(position.data()), glm::make_vec3(normal.data()) }); } for (uint32_t idx : magnum_indices) indices.push_back(idx); return loader.loadMesh(move(vertices), move(indices)); }; // meshes { initPrimitives(meshData); for (const auto &[drawable, data] : meshData) { meshIndices[drawable] = int(meshes.size()); meshes.emplace_back(convertMesh(data)); } } // Materials { const auto palette = envs.front()->getPalette(); constexpr float shininess = 300.0f; for (auto c : palette) { materialIndices[c] = int(materials.size()); materials.emplace_back(loader.makeMaterial(MaterialParams { glm::vec3(c.r(), c.g(), c.b()), glm::vec3(1.f), shininess })); } } // Scene { v4r::SceneDescription scene_desc(move(meshes), move(materials)); scene_desc.addLight(glm::vec3(0, 4, 2), glm::vec3(0.66f)); scene = loader.makeScene(scene_desc); } // vector of render envs { auto [fov, near, far, aspectRatio] = agentCameraParameters(); UNUSED(aspectRatio); for (auto &env : envs) for (int agentIdx = 0; agentIdx < env->getNumAgents(); ++agentIdx) renderEnvs.emplace_back(cmdStream.makeEnvironment(scene, fov, near, far)); } pixelsPerFrame = framebufferSize.x * framebufferSize.y * 4; pixelsPerEnv = envs.front()->getNumAgents() * pixelsPerFrame; } V4REnvRenderer::Impl::~Impl() { TLOG(INFO) << __PRETTY_FUNCTION__; } void V4REnvRenderer::Impl::reset(Env &env, int envIdx) { auto [fov, near, far, aspectRatio] = agentCameraParameters(); if (withOverviewCamera && envIdx == 0) { auto [oFov, oNear, oFar, oAspectRatio] = overviewCameraParameters(); fov = oFov, near = oNear, far = oFar, aspectRatio = oAspectRatio; } UNUSED(aspectRatio); for (int i = 0; i < env.getNumAgents(); ++i) { const auto idx = envIdx * env.getNumAgents() + i; // assuming all envs have the same numAgents renderEnvs[idx] = cmdStream.makeEnvironment(scene, fov, near, far); } // reset renderer data structures { envDrawables[envIdx] = SceneGraph::DrawableGroup3D{}; } // drawables { const auto &drawables = env.getDrawables(); for (int agentIdx = 0; agentIdx < env.getNumAgents(); ++agentIdx) { const auto renderEnvIdx = envIdx * env.getNumAgents() + agentIdx; auto &renderEnv = renderEnvs[renderEnvIdx]; for (const auto &[drawableType, meshIndex] : meshIndices) { for (const auto &sceneObjectInfo : drawables.at(drawableType)) { const auto &color = sceneObjectInfo.color; const auto materialIdx = materialIndices[color]; // if we forgot to add the color to the palette, we should crash here const auto renderID = renderEnv.addInstance(uint32_t(meshIndex), uint32_t(materialIdx), glm::mat4(1.f)); sceneObjectInfo.objectPtr->addFeature(renderEnv, renderID, envDrawables[envIdx]); } } } drawablesObjects[envIdx].clear(), v4rDrawables[envIdx].clear(); for (size_t i = 0; i < envDrawables[envIdx].size(); ++i) { auto &object = envDrawables[envIdx][i].object(); drawablesObjects[envIdx].emplace_back(object); auto v4rDrawable = dynamic_cast(&envDrawables[envIdx][i]); v4rDrawables[envIdx].emplace_back(v4rDrawable); } dirtyDrawables[envIdx].clear(); } // controllable overview camera if (withOverviewCamera && envIdx == 0) overview.reset(&env.getScene()); } void V4REnvRenderer::Impl::preDraw(Env &env, int envIdx) { dirtyDrawables[envIdx].clear(); const auto numAgents = env.getNumAgents(); for (int agentIdx = 0; agentIdx < numAgents; ++agentIdx) { const auto renderEnvIdx = envIdx * numAgents + agentIdx; v4r::Environment &renderEnv = renderEnvs[renderEnvIdx]; auto activeCameraPtr = env.getAgents()[agentIdx]->getCamera(); if (withOverviewCamera && overview.enabled && envIdx == 0) activeCameraPtr = overview.camera; auto view = glm::make_mat4(activeCameraPtr->cameraMatrix().data()); renderEnv.setCameraView(view); } if (previousRenderer) { dirtyDrawables[envIdx] = previousRenderer->getDirtyDrawables(envIdx); } else { std::vector> dirtyObjects; for (size_t i = 0; i < drawablesObjects[envIdx].size(); ++i) { auto &obj = drawablesObjects[envIdx][i].get(); if (obj.isDirty()) { dirtyObjects.emplace_back(drawablesObjects[envIdx][i]); dirtyDrawables[envIdx].emplace_back(i); } } // Collapsing the scene graph transformations "manually", somehow this is barely faster, if at all SceneGraph::AbstractObject3D::setClean(dirtyObjects); } for (auto &drawableIdx : dirtyDrawables[envIdx]) v4rDrawables[envIdx][drawableIdx]->updateAbsoluteTransformation(); } void V4REnvRenderer::Impl::draw(Envs &) { // rdoc.startFrame(); cmdStream.render(renderEnvs); cmdStream.waitForFrame(); // memcpy( // cpuFrames.data(), // cmdStream.getRGB(), // env.getNumAgents() * framebufferSize.x * framebufferSize.y * 4 // ); // rdoc.endFrame(); // cudaError_t cuda_res = cudaStreamSynchronize(cudaStream); // if (cuda_res != cudaSuccess) // abort(); } const uint8_t * V4REnvRenderer::Impl::getObservation(int envIdx, int agentIdx) const { const auto startIdx = envIdx * pixelsPerEnv; return cmdStream.getRGB() + startIdx + agentIdx * pixelsPerFrame; // return cpuFrames.data() + agentIdx * framebufferSize.x * framebufferSize.y * 4; } V4REnvRenderer::V4REnvRenderer(Envs &envs, int w, int h, V4REnvRenderer *previousRenderer, bool withOverview) { pimpl = std::make_unique(envs, w, h, previousRenderer, withOverview); } V4REnvRenderer::~V4REnvRenderer() = default; void V4REnvRenderer::reset(Env &env, int envIdx) { pimpl->reset(env, envIdx); } void V4REnvRenderer::preDraw(Env &env, int envIndex) { pimpl->preDraw(env, envIndex); } void V4REnvRenderer::draw(Envs &envs) { pimpl->draw(envs); } const uint8_t * V4REnvRenderer::getObservation(int envIdx, int agentIdx) const { return pimpl->getObservation(envIdx, agentIdx); } std::vector V4REnvRenderer::getDirtyDrawables(int envIdx) const { return pimpl->getDirtyDrawables(envIdx); } Overview * V4REnvRenderer::getOverview() { return pimpl->getOverview(); } ================================================ FILE: src/libs/viewer/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.10) project(libviewer VERSION 0.1 LANGUAGES CXX) if (BUILD_GUI_APPS) find_package(Corrade REQUIRED Main) set(MAGNUM_DEPENDENCIES Corrade::Main Magnum::GL Magnum::Magnum Magnum::MeshTools Magnum::Primitives Magnum::SceneGraph Magnum::Shaders) add_library_default(viewer) target_link_libraries(viewer PUBLIC env magnum_rendering ${MAGNUM_DEPENDENCIES} Magnum::Trade Magnum::Application ${OpenCV_LIBS}) if (NOT CORRADE_TARGET_APPLE) target_link_libraries(viewer PUBLIC v4r_rendering) endif () endif() ================================================ FILE: src/libs/viewer/include/viewer/viewer.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Megaverse { class Viewer : public Magnum::Platform::Application { public: explicit Viewer(Envs &envs, bool useVulkan, EnvRenderer *parentRenderer, const Arguments &arguments); virtual ~Viewer() { viewerExists = false; } public: void step(const std::vector &dones); protected: void drawEvent() override; void keyPressEvent(KeyEvent &event) override; void keyReleaseEvent(KeyEvent &event) override; void mouseMoveEvent(MouseMoveEvent &event) override; private: void controlOverview(const KeyEvent::Key &key, bool addAction); void moveOverviewCamera(); public: static bool viewerExists; protected: Envs &envs; /** * Overview is only supported for the 1st env in case we have a vector of envs. * On-the-fly switching between multiple envs is also possible, but requires additional logic. */ constexpr static int activeEnv = 0; int activeAgent = 0; std::unique_ptr renderer; bool forceReset = false; private: bool useVulkan; int width = 1920, height = 1080; std::unique_ptr ctx; bool withDebugDraw = true; Action currOverviewAction = Action::Idle; Magnum::GL::Framebuffer framebuffer{Magnum::NoCreate}; Magnum::GL::Renderbuffer colorBuffer{Magnum::NoCreate}; }; } ================================================ FILE: src/libs/viewer/src/viewer.cpp ================================================ #include #include #include #include #include #include #ifndef CORRADE_TARGET_APPLE #include #endif using namespace Magnum; using namespace Megaverse; bool Viewer::viewerExists = false; Viewer::Viewer(Envs &envs, bool useVulkan, EnvRenderer *parentRenderer, const Arguments& arguments) : Magnum::Platform::Application{arguments, NoCreate} , envs{envs} , useVulkan{useVulkan} { assert(!viewerExists); // only one viewer per process is supported viewerExists = true; // Try 8x MSAA, fall back to zero samples if not possible. Enable only 2x MSAA if we have enough DPI. { const Vector2 dpiScaling = this->dpiScaling({}); Configuration conf; conf.setTitle("MegaverseViewer").setSize({width, height}, dpiScaling); GLConfiguration glConf; glConf.setSampleCount(dpiScaling.max() < 2.0f ? 8 : 2); if(!tryCreate(conf, glConf)) { TLOG(WARNING) << "Fall back to default MSAA"; create(conf, glConf.setSampleCount(0)); } } GL::Renderer::enable(GL::Renderer::Feature::DepthTest); GL::Renderer::enable(GL::Renderer::Feature::FaceCulling); ctx = std::make_unique(); const Magnum::Vector2i fbSize = framebufferSize(); if (useVulkan) { #if defined (CORRADE_TARGET_APPLE) TLOG(ERROR) << "Vulkan not supported on MacOS"; UNUSED(parentRenderer); #else framebuffer = GL::Framebuffer{Range2Di{{}, fbSize}}; framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, colorBuffer); framebuffer.mapForDraw({{0, GL::Framebuffer::ColorAttachment{0}}}); framebuffer.clearColor(0, Color3{0.125f}).clearDepth(1.0).bind(); auto v4rParentRenderer = dynamic_cast(parentRenderer); renderer = std::make_unique(envs, fbSize[0], fbSize[1], v4rParentRenderer, true); #endif } else { renderer = std::make_unique(envs, fbSize[0], fbSize[1], withDebugDraw, true, ctx.get()); dynamic_cast(*renderer).toggleDebugMode(); } for (int envIdx = 0; envIdx < int(envs.size()); ++envIdx) renderer->reset(*envs[envIdx], envIdx); setSwapInterval(0); TLOG(WARNING) << "\nControls:\n" << "WASD and arrow keys to control the agent\n" << "1,2,3,4,etc. to switch between agents (if several are present in the environment)\n" << "Press O to toggle the overview camera, use mouse to control view angle\n" << "Use UHJK keys to control the position of the camera\n" << "Press R to reset the episode\n" << "Press ENTER to toggle Bullet collision debug view (only OpenGL version)\n" << "ESC to exit the app\n"; } void Viewer::step(const std::vector &dones) { for (int envIdx = 0; envIdx < int(envs.size()); ++envIdx) { if (forceReset) envs[envIdx]->terminateEpisodeOnNextFrame(); if (dones[envIdx]) renderer->reset(*envs[envIdx], envIdx); } forceReset = false; moveOverviewCamera(); redraw(); } void Viewer::drawEvent() { if (useVulkan) { #if !defined(CORRADE_TARGET_APPLE) for (int envIdx = 0; envIdx < int(envs.size()); ++envIdx) renderer->preDraw(*envs[envIdx], envIdx); renderer->draw(envs); auto dataPtr = renderer->getObservation(activeEnv, activeAgent); // probably would've been easier to actually draw a quad upside down instead of flipping the texture // but hey, this is not a performance-critical code and it works! cv::Mat mat(height, width, CV_8UC4, (char *) dataPtr); cv::flip(mat, mat, 0); Containers::ArrayView data(dataPtr, width * height * 4); ImageView2D image(PixelFormat::RGBA8Unorm, {width, height}, data); GL::Texture2D texture; texture.setWrapping(GL::SamplerWrapping::ClampToEdge) .setStorage(int(Math::log2(height)) + 1, GL::TextureFormat::RGBA8, {width, height}) .setSubImage(0, {}, image) .generateMipmap(); framebuffer.mapForRead(GL::Framebuffer::ColorAttachment(0)); framebuffer.attachTexture(GL::Framebuffer::ColorAttachment(0), texture, 0); // blit color to window framebuffer GL::defaultFramebuffer.bind(); GL::AbstractFramebuffer::blit(framebuffer, GL::defaultFramebuffer, {{}, framebuffer.viewport().size()}, GL::FramebufferBlit::Color); #endif } else { auto &magnumRenderer = dynamic_cast(*renderer); for (int envIdx = 0; envIdx < int(envs.size()); ++envIdx) { magnumRenderer.preDraw(*envs[envIdx], envIdx); magnumRenderer.drawAgent(*envs[envIdx], envIdx, activeAgent, false); } auto rendererFramebuffer = magnumRenderer.getFramebuffer(); GL::defaultFramebuffer.bind(); rendererFramebuffer->mapForRead(GL::Framebuffer::ColorAttachment{0}); // blit color to window framebuffer GL::AbstractFramebuffer::blit(*rendererFramebuffer, GL::defaultFramebuffer, {{}, rendererFramebuffer->viewport().size()}, GL::FramebufferBlit::Color); } swapBuffers(); } void Viewer::keyPressEvent(Platform::Sdl2Application::KeyEvent &event) { auto *overview = renderer->getOverview(); controlOverview(event.key(), true); auto chgAgent = [this](int idx) { if (idx >= envs[0]->getNumAgents()) TLOG(WARNING) << "Could not switch to agent " << idx << " (greater than numAgents)"; else activeAgent = idx; }; switch (event.key()) { case KeyEvent::Key::One: chgAgent(0); break; case KeyEvent::Key::Two: chgAgent(1); break; case KeyEvent::Key::Three: chgAgent(2); break; case KeyEvent::Key::Four: chgAgent(3); break; case KeyEvent::Key::Five: chgAgent(4); break; case KeyEvent::Key::Six: chgAgent(5); break; case KeyEvent::Key::R: forceReset = true; break; case KeyEvent::Key::O: overview->enabled = !overview->enabled; setCursor(overview->enabled ? Cursor::HiddenLocked : Cursor::Arrow); break; case KeyEvent::Key::Enter: if (!useVulkan) dynamic_cast(*renderer).toggleDebugMode(); break; case KeyEvent::Key::Esc: setCursor(Cursor::Arrow); exit(0); std::exit(0); default: break; } event.setAccepted(); } void Viewer::keyReleaseEvent(Platform::Sdl2Application::KeyEvent &event) { controlOverview(event.key(), false); event.setAccepted(); } void Viewer::mouseMoveEvent(Platform::Sdl2Application::MouseMoveEvent &event) { auto *overview = renderer->getOverview(); if (!overview->enabled) return; constexpr float sensitivity = 0.075; const auto horizontalMove = float(event.relativePosition().x()); const auto verticalMove = -float(event.relativePosition().y()); const auto yRotation = Deg(horizontalMove); overview->root->rotateYLocal(-sensitivity * yRotation); const auto verticalRotation = sensitivity * verticalMove; auto newRotation = overview->verticalRotation + verticalRotation; newRotation = std::min(newRotation, 89.0f); newRotation = std::max(newRotation, -89.0f); const auto rotationDelta = newRotation - overview->verticalRotation; const auto xRotation = Deg(rotationDelta); overview->verticalTilt->rotateXLocal(xRotation); overview->verticalRotation = newRotation; overview->saveTransformation(); event.setAccepted(); redraw(); } void Viewer::controlOverview(const KeyEvent::Key &key, bool addAction) { auto a = Action::Idle; switch(key) { case KeyEvent::Key::U: a = Action::Forward; break; case KeyEvent::Key::J: a = Action::Backward; break; case KeyEvent::Key::H: a = Action::Left; break; case KeyEvent::Key::K: a = Action::Right; break; case KeyEvent::Key::LeftShift: a = Action::LookUp; break; case KeyEvent::Key::RightShift: a = Action::LookDown; break; default: break; } if (a == Action::Idle) return; if (addAction) currOverviewAction |= a; else currOverviewAction &= ~a; redraw(); } void Viewer::moveOverviewCamera() { auto *overview = renderer->getOverview(); if (!overview->enabled) return; const float speed = 0.6; Vector3 moveDirection{}; const auto backwardDirection = overview->verticalTilt->absoluteTransformation().backward(); if (!!(currOverviewAction & Action::Forward)) moveDirection -= backwardDirection; else if (!!(currOverviewAction & Action::Backward)) moveDirection += backwardDirection; const auto rightDirection = overview->verticalTilt->absoluteTransformation().right(); if (!!(currOverviewAction & Action::Left)) moveDirection -= rightDirection; else if (!!(currOverviewAction & Action::Right)) moveDirection += rightDirection; const auto upDirection = Vector3{0, 1, 0}; if (!!(currOverviewAction & Action::LookUp)) moveDirection += upDirection; else if (!!(currOverviewAction & Action::LookDown)) moveDirection -= upDirection; if (moveDirection.length() > FLT_EPSILON) overview->root->translate(speed * moveDirection.normalized()); overview->saveTransformation(); } ================================================ FILE: src/test/CMakeLists.txt ================================================ add_test_default(run_unit_tests) target_link_libraries(run_unit_tests gtest gtest_main util magnum_rendering env mazes scenarios) ================================================ FILE: src/test/src/env_tests.cpp ================================================ #include #include #include #include #include using namespace Megaverse; class EnvTest : public ::testing::Test { protected: virtual void SetUp() { scenariosGlobalInit(); } }; TEST_F(EnvTest, env) { Env env{"TowerBuilding"}; } TEST_F(EnvTest, multipleEnvs) { Envs envs; for (int i = 0; i < 3; ++i) { auto e = std::make_unique("TowerBuilding"); e->reset(); envs.emplace_back(std::move(e)); } MagnumEnvRenderer renderer{envs, 128, 72}; for (int i = 0; i < 3; ++i) renderer.reset(*envs[i], i); for (int i = 0; i < 3; ++i) renderer.draw(envs); } ================================================ FILE: src/test/src/filesystem_tests.cpp ================================================ #include #include #include namespace Megaverse { TEST(filesystem, pathJoin) { const std::string t1{"abc"}, t2{"def"}, t3{"ghi"}; const auto p1{pathJoin(t1)}, p2{pathJoin(t1, t2)}, p3{pathJoin(t1, t2, t3)}; EXPECT_EQ(p1, t1); EXPECT_EQ(p2, t1 + pathDelim() + t2); EXPECT_EQ(p3, t1 + pathDelim() + t2 + pathDelim() + t3); } } ================================================ FILE: src/test/src/gfx_tests.cpp ================================================ #include #include using namespace Megaverse; TEST(gfx, context) { // this will only work if the GPU is present constexpr int device = 0; WindowlessContext context{device}; } ================================================ FILE: src/test/src/logger_tests.cpp ================================================ #include #include #include #include using namespace Megaverse; #define TST_LOG(level) LogMessage(level, __FILE__, __LINE__, __FUNCTION__, &testStream)() namespace { // helper functions void testLog(std::ostringstream &testStream, int threadIdx) { constexpr int n = 5000; for (int i = 0; i < n; ++i) TST_LOG(INFO) << "thread_" << threadIdx; } } /// Let multiple threads write log messages to the same stream, /// then parse the output and verify that none of the output is messed up /// (e.g. no overlapping messages "thread_thread_21, etc.) TEST(logger, concurrency) { // stream that stores log output during this test, so we can actually verify the results std::ostringstream testStream; constexpr int numThreads = 10; std::vector threads; threads.reserve(numThreads); for (int i = 0; i < numThreads; ++i) threads.emplace_back(testLog, std::ref(testStream), i + 1); for (int i = 0; i < numThreads; ++i) threads[i].join(); std::vector lines = Megaverse::splitString(testStream.str(), "\n"); int threadIdx = 0; for (auto &line : lines) { const auto msg = splitString(line, " ").back(); EXPECT_TRUE(startsWith(msg, "thread_")); EXPECT_LE(msg.length(), 10); std::istringstream(splitString(msg, "_").back()) >> threadIdx; EXPECT_GE(threadIdx, 1); EXPECT_LE(threadIdx, numThreads); } } ================================================ FILE: src/test/src/maze_tests.cpp ================================================ #include #include #include #include using namespace Megaverse; TEST(maze, honeycomb) { const auto size = 5; auto *maze = new HoneyCombMaze{size}; auto *algorithm = new Kruskal; TLOG(DEBUG) << "Initialising graph..."; maze->InitialiseGraph(); TLOG(DEBUG) << "Generating maze..."; maze->GenerateMaze(algorithm); const auto outputprefix = "/tmp/maze"; TLOG(DEBUG) << "Rendering maze to '" << outputprefix << ".svg'..."; maze->PrintMazeSVG(outputprefix); auto adjList = maze->getAdjacencyList(); const auto [xmin, ymin, xmax, ymax] = maze->GetCoordinateBounds(); TLOG(DEBUG) << xmin << " " << ymin << " " << xmax << " " << ymax; TLOG(DEBUG) << system("eog /tmp/maze.svg"); } ================================================ FILE: src/test/src/noise_test.cpp ================================================ #include #include #include #include #include #include #include using namespace Megaverse; # pragma pack (push, 1) struct BMPHeader { std::uint16_t bfType; std::uint32_t bfSize; std::uint16_t bfReserved1; std::uint16_t bfReserved2; std::uint32_t bfOffBits; std::uint32_t biSize; std::int32_t biWidth; std::int32_t biHeight; std::uint16_t biPlanes; std::uint16_t biBitCount; std::uint32_t biCompression; std::uint32_t biSizeImage; std::int32_t biXPelsPerMeter; std::int32_t biYPelsPerMeter; std::uint32_t biClrUsed; std::uint32_t biClrImportant; }; static_assert(sizeof(BMPHeader) == 54); # pragma pack (pop) struct RGB { double r = 0.0; double g = 0.0; double b = 0.0; constexpr RGB() = default; explicit constexpr RGB(double _rgb) noexcept : r(_rgb), g(_rgb), b(_rgb) {} constexpr RGB(double _r, double _g, double _b) noexcept : r(_r), g(_g), b(_b) {} }; class Image { private: std::vector m_data; std::int32_t m_width = 0, m_height = 0; bool inBounds(std::int32_t y, std::int32_t x) const noexcept { return (0 <= y) && (y < m_height) && (0 <= x) && (x < m_width); } static constexpr std::uint8_t ToUint8(double x) noexcept { return x >= 1.0 ? 255 : x <= 0.0 ? 0 : static_cast(x * 255.0 + 0.5); } public: Image() = default; Image(std::size_t width, std::size_t height) : m_data(width * height) , m_width(static_cast(width)) , m_height(static_cast(height)) {} void set(std::int32_t x, std::int32_t y, const RGB& color) { if (!inBounds(y, x)) { return; } m_data[static_cast(y) * m_width + x] = color; } std::int32_t width() const { return m_width; } std::int32_t height() const { return m_height; } bool saveBMP(const std::string& path) { const std::int32_t rowSize = m_width * 3 + m_width % 4; const std::uint32_t bmpsize = rowSize * m_height; const BMPHeader header = { 0x4d42, static_cast(bmpsize + sizeof(BMPHeader)), 0, 0, sizeof(BMPHeader), 40, m_width, m_height, 1, 24, 0, bmpsize, 0, 0, 0, 0 }; if (std::ofstream ofs{ path, std::ios_base::binary }) { ofs.write(reinterpret_cast(&header), sizeof(header)); std::vector line(rowSize); for (std::int32_t y = m_height - 1; -1 < y; --y) { size_t pos = 0; for (std::int32_t x = 0; x < m_width; ++x) { const RGB& col = m_data[static_cast(y) * m_width + x]; line[pos++] = ToUint8(col.b); line[pos++] = ToUint8(col.g); line[pos++] = ToUint8(col.r); } ofs.write(reinterpret_cast(line.data()), line.size()); } return true; } else { return false; } } }; // //void testNoise() //{ // siv::PerlinNoise perlinA(std::random_device{}); // siv::PerlinNoise perlinB; // // std::array state; // perlinA.serialize(state); // perlinB.deserialize(state); // // assert(perlinA.octaveNoise(0.1, 0.2, 0.3, 4) // == perlinB.octaveNoise(0.1, 0.2, 0.3, 4)); // // perlinA.reseed(1234); // perlinB.reseed(1234); // // assert(perlinA.octaveNoise(0.1, 0.2, 0.3, 4) // == perlinB.octaveNoise(0.1, 0.2, 0.3, 4)); // // perlinA.reseed(std::mt19937{ 1234 }); // perlinB.reseed(std::mt19937{ 1234 }); // // assert(perlinA.octaveNoise(0.1, 0.2, 0.3, 4) // == perlinB.octaveNoise(0.1, 0.2, 0.3, 4)); //} TEST(util, perlinNoise) { // testNoise(); Image image(51, 21); std::cout << "---------------------------------\n"; std::cout << "* frequency [0.1 .. 8.0 .. 64.0] \n"; std::cout << "* octaves [1 .. 8 .. 16] \n"; std::cout << "* seed [0 .. 2^32-1] \n"; std::cout << "---------------------------------\n"; Megaverse::Rng rng{std::random_device{}()}; for (int i = 0; i < 10; ++i) { double frequency = double (randRange(1, 60, rng)) / 10.0; std::cout << "double frequency = " << frequency << "\n"; frequency = std::clamp(frequency, 0.1, 64.0); std::int32_t octaves = randRange(1, 4, rng); std::cout << "int32 octaves = " << octaves << "\n"; octaves = std::clamp(octaves, 1, 16); std::uint32_t seed = randRange(0, 1000000000, rng); std::cout << "uint32 seed = " << seed << "\n"; const siv::PerlinNoise perlin(seed); const double fx = image.width() / frequency; const double fy = image.height() / frequency; for (std::int32_t y = 0; y < image.height(); ++y) { for (std::int32_t x = 0; x < image.width(); ++x) { const RGB color(perlin.accumulatedOctaveNoise2D_0_1(x / fx, y / fy, octaves)); image.set(x, y, color); } } std::stringstream ss; ss << 'f' << frequency << 'o' << octaves << '_' << seed << ".bmp"; if (image.saveBMP(ss.str())) { std::cout << "...saved \"" << ss.str() << "\"\n"; } else { std::cout << "...failed\n"; } } } ================================================ FILE: src/test/src/string_tests.cpp ================================================ #include #include using namespace Megaverse; TEST(str, startsWith) { EXPECT_TRUE(startsWith("abc", "a")); EXPECT_TRUE(startsWith("abc", "ab")); EXPECT_TRUE(startsWith("abc", "abc")); EXPECT_TRUE(startsWith("abc", "")); EXPECT_FALSE(startsWith("abc", "abcd")); EXPECT_FALSE(startsWith("abc", "b")); EXPECT_TRUE(startsWith("", "")); EXPECT_FALSE(startsWith("", "a")); } TEST(str, endsWith) { EXPECT_TRUE(endsWith("abc", "c")); EXPECT_TRUE(endsWith("abc", "bc")); EXPECT_TRUE(endsWith("abc", "abc")); EXPECT_TRUE(endsWith("abc", "")); EXPECT_FALSE(endsWith("abc", "_abc")); EXPECT_FALSE(endsWith("abc", "b")); EXPECT_TRUE(endsWith("", "")); EXPECT_FALSE(endsWith("", "a")); } ================================================ FILE: src/test/src/util_tests.cpp ================================================ #include #include #include using namespace Megaverse; TEST(util, memcpyStride) { char buf[] = { 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1 }; char zeros[9], ones[6]; memcpyStride(zeros, buf, 3, 3, 0, 5); auto range = std::equal_range(std::begin(zeros), std::end(zeros), 0); EXPECT_TRUE(range.first == std::begin(zeros) && range.second == std::end(zeros)); memcpyStride(ones, buf, 2, 3, 3, 5); range = std::equal_range(std::begin(ones), std::end(ones), 1); EXPECT_TRUE(range.first == std::begin(ones) && range.second == std::end(ones)); } ================================================ FILE: src/test/src/voxel_grid_tests.cpp ================================================ #include #include #include using namespace Megaverse; struct TestVoxelState { int someInt; std::string someString; }; TEST(voxelGrid, basic) { VoxelGrid vg{100, {0, 0, 0}, 1}; VoxelCoords coords{1, 2, 3}; EXPECT_EQ(coords, VoxelCoords(1, 2, 3)); // make sure operator== works EXPECT_EQ(vg.getCoords({1.5, 2.3, 3.2}), coords); auto voxelPtr = vg.get({0, 0, 0}); EXPECT_EQ(voxelPtr, nullptr); TestVoxelState state{3, "42"}; vg.set({0, 0, 0}, state); voxelPtr = vg.get({0, 0, 0}); EXPECT_NE(voxelPtr, nullptr); const auto newStr = "I love voxels!"; voxelPtr->someInt = 42; voxelPtr->someString = newStr; auto ptr = vg.get({0, 0, 0}); EXPECT_EQ(voxelPtr, ptr); EXPECT_EQ(ptr->someInt, 42); EXPECT_EQ(ptr->someString, newStr); } TEST(voxelGrid, perf) { VoxelGrid vg{100, {0, 0, 0}, 1}; for (int x = 0; x < 100; ++x) for (int y = 0; y < 100; ++y) for (int z = 0; z < 100; ++z) vg.set({x, y, z}, {x + y + z, "42"}); } TEST(voxelGrid, voxelState) { VoxelGrid vg{0, {0, 0, 0}, 1}; vg.set({0, 1, 2}, VoxelState{}); TLOG(INFO) << sizeof(vg) << " " << sizeof(*vg.get({0, 1, 2})); }