├── .clang-format ├── .clang-tidy ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.yaml ├── docs │ ├── CLionQuickStart │ │ ├── CLionQuickStart.md │ │ ├── CMake_config.png │ │ ├── GH_list.png │ │ ├── VCS_menu.png │ │ ├── build.png │ │ ├── debug.png │ │ ├── img.png │ │ ├── mainUI.png │ │ ├── project_menu.png │ │ └── run.png │ └── Code-Styles-and-Guidelines.md └── workflows │ ├── build.yml │ ├── check.yml │ ├── docs.yml │ └── post.yml ├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets ├── audio │ ├── Click.wav │ └── testbgm.mp3 ├── fonts │ └── Inter.ttf ├── icon.jpg ├── shaders │ ├── Base.frag │ ├── Base.vert │ ├── Triangle.frag │ └── Triangle.vert └── sprites │ ├── cat │ ├── cat-0.bmp │ ├── cat-1.bmp │ ├── cat-2.bmp │ ├── cat-3.bmp │ ├── cat-4.bmp │ ├── cat-5.bmp │ ├── cat-6.bmp │ └── cat-7.bmp │ └── giraffe.png ├── cmake └── Dependencies.cmake ├── docs ├── Doxyfile ├── custom.css ├── delete_me.css ├── delete_me.html ├── doxygen-awesome │ ├── doxygen-awesome-darkmode-toggle.js │ ├── doxygen-awesome-sidebar-only-darkmode-toggle.css │ ├── doxygen-awesome-sidebar-only.css │ └── doxygen-awesome.css └── header.html ├── example ├── include │ ├── App.hpp │ ├── Cat.hpp │ ├── Giraffe.hpp │ └── GiraffeText.hpp └── src │ ├── App.cpp │ ├── Cat.cpp │ ├── Giraffe.cpp │ ├── GiraffeText.cpp │ └── main.cpp ├── include ├── Core │ ├── Context.hpp │ ├── DebugMessageCallback.hpp │ ├── Docs.hpp │ ├── Drawable.hpp │ ├── IndexBuffer.hpp │ ├── MissingFontTextureBase64.hpp │ ├── MissingImageTextureBase64.hpp │ ├── Program.hpp │ ├── Shader.hpp │ ├── Texture.hpp │ ├── TextureUtils.hpp │ ├── UniformBuffer.hpp │ ├── UniformBuffer.inl │ ├── VertexArray.hpp │ └── VertexBuffer.hpp ├── Util │ ├── Animation.hpp │ ├── AssetStore.hpp │ ├── AssetStore.inl │ ├── BGM.hpp │ ├── Base64.hpp │ ├── Color.hpp │ ├── Docs.hpp │ ├── GameObject.hpp │ ├── Image.hpp │ ├── Input.hpp │ ├── Keycode.hpp │ ├── LoadTextFile.hpp │ ├── Logger.hpp │ ├── MissingTexture.hpp │ ├── Position.hpp │ ├── Position.inl │ ├── Renderer.hpp │ ├── SFX.hpp │ ├── Text.hpp │ ├── Time.hpp │ ├── Transform.hpp │ └── TransformUtils.hpp ├── config.hpp └── pch.hpp ├── src ├── Core │ ├── Context.cpp │ ├── DebugMessageCallback.cpp │ ├── IndexBuffer.cpp │ ├── Program.cpp │ ├── Shader.cpp │ ├── Texture.cpp │ ├── TextureUtils.cpp │ ├── VertexArray.cpp │ └── VertexBuffer.cpp ├── Util │ ├── Animation.cpp │ ├── BGM.cpp │ ├── Color.cpp │ ├── GameObject.cpp │ ├── Image.cpp │ ├── Input.cpp │ ├── LoadTextFile.cpp │ ├── Logger.cpp │ ├── MissingTexture.cpp │ ├── Position.cpp │ ├── Renderer.cpp │ ├── SFX.cpp │ ├── Text.cpp │ ├── Time.cpp │ └── TransformUtils.cpp └── config.cpp └── test ├── Interactive └── Audio.cpp ├── NotSimpleTest.cpp ├── SimpleTest.cpp └── TransformTest.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | AccessModifierOffset: -4 4 | AllowShortFunctionsOnASingleLine: Inline 5 | AlwaysBreakTemplateDeclarations: Yes 6 | PackConstructorInitializers: Never 7 | InsertNewlineAtEOF: true 8 | BreakAfterAttributes: Always 9 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | WarningsAsErrors: '-*, 2 | bugprone-*,-bugprone-easily-swappable-parameters, 3 | readability-*,-readability-convert-member-functions-to-static,-readability-redundant-access-specifiers,-readability-magic-numbers, 4 | modernize-*,-modernize-use-trailing-return-type,-modernize-use-nodiscard' 5 | CheckOptions: 6 | - { key: readability-identifier-naming.NamespaceCase, value: CamelCase } 7 | - { key: readability-identifier-naming.ClassCase, value: CamelCase } 8 | - { key: readability-identifier-naming.MemberCase, value: CamelCase } 9 | - { key: readability-identifier-naming.MemberPrefix, value: m_ } 10 | - { key: readability-identifier-naming.ClassMemberCase, value: CamelCase } 11 | - { key: readability-identifier-naming.ClassMemberPrefix, value: s_ } 12 | - { key: readability-identifier-naming.StructCase, value: CamelCase } 13 | - { key: readability-identifier-naming.EnumCase, value: CamelCase } 14 | - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } 15 | - { key: readability-identifier-naming.FunctionCase, value: CamelCase } 16 | - { key: readability-identifier-naming.VariableCase, value: camelBack } 17 | - { key: readability-identifier-naming.ParameterCase, value: camelBack } 18 | - { key: readability-identifier-naming.TypeAliasCase, value: CamelCase } 19 | - { key: readability-identifier-naming.TypedefCase, value: CamelCase } 20 | - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } 21 | - { key: readability-identifier-naming.TemplateParameterCase, value: UPPER_CASE } 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.vert text 4 | *.frag text 5 | 6 | *.png binary 7 | *.jpg binary 8 | *.jpeg binary 9 | *.bmp binary 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a problem in PTSD 3 | labels: [bug] 4 | body: 5 | 6 | - type: markdown 7 | attributes: 8 | value: | 9 | *Before reporting:* 10 | - Confirm the problem is reproducible on **master** or **latest stable** release 11 | - Search [existing issues](https://github.com/ntut-open-source-club/practical-tools-for-simple-design/issues?q=is%3Aissue+label%3Abug) 12 | - type: textarea 13 | attributes: 14 | label: "Problem" 15 | description: "Describe the current behavior. May include logs, images, or videos." 16 | validations: 17 | required: true 18 | - type: textarea 19 | attributes: 20 | label: "Steps to reproduce" 21 | description: | 22 | - If the bug pertains to crashing (or segfault), please include a stacktrace. 23 | validations: 24 | required: true 25 | - type: textarea 26 | attributes: 27 | label: "Expected behavior" 28 | description: "Describe the behavior you expect." 29 | validations: 30 | required: true 31 | 32 | - type: input 33 | attributes: 34 | label: "PTSD version" 35 | placeholder: "release 1.0.0 or commit 013246df758a14ea083bfbe7726a589db19a560a" 36 | validations: 37 | required: true 38 | - type: input 39 | attributes: 40 | label: "Operating system/version" 41 | placeholder: "macOS 11.5" 42 | validations: 43 | required: true 44 | - type: input 45 | attributes: 46 | label: "CMake version" 47 | placeholder: "3.27.8" 48 | validations: 49 | required: true 50 | - type: textarea 51 | attributes: 52 | label: "CMakeCache.txt" 53 | description: | 54 | Can found in the build dir: 55 | 56 | - CLion (`cmake-build-debug/CMakeCache.txt`) 57 | - Visual Studio (`out\build\x64-Debug\CMakeCache.txt`) 58 | - Others (`build/CMakeCache.txt`) 59 | 60 | Please use [Attach File](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/attaching-files), do not copy paste whole file. 61 | validations: 62 | required: true 63 | 64 | - type: textarea 65 | attributes: 66 | label: "OpenGL Info" 67 | description: "The first thing printed in the log when you run the program." 68 | placeholder: | 69 | [info] OpenGL Info 70 | [info] Vendor: Apple 71 | [info] Renderer: Apple M2 72 | [info] Version: 4.1 Metal - 88 73 | [info] GLSL Version: 4.10 74 | render: txt 75 | validations: 76 | required: true 77 | -------------------------------------------------------------------------------- /.github/docs/CLionQuickStart/CLionQuickStart.md: -------------------------------------------------------------------------------- 1 | ### CLion Quick Start 2 | 3 | The reader is encouraged to read 4 | the [official guide](https://www.jetbrains.com/help/clion/clion-quick-start-guide.html#before-start). 5 | 6 | #### Install CLion 7 | 8 | * Read [this](https://hackmd.io/FNdnGXTgSyOsjZk0XSEIzA?both#%E4%BD%BF%E7%94%A8-JB-Toolbox-%E5%AE%89%E8%A3%9D) if you 9 | have trouble installing it or for some random knowledge. 10 | 11 | #### Clone the project from remote 12 | 13 | 1. Press the `Get from VCS` button at the top-right of project menu. ![project menu](./project_menu.png) 14 | 2. Clone the repo. 15 | * Paste the url of the repo and click `clone`.![VCS_menu.png](./VCS_menu.png) 16 | * Alternately, the user may log in their GitHub or GitLab account to list all their 17 | repos.![GH_list.png](./GH_list.png) 18 | 3. Post-clone configs 19 | ![CMake_config.png](./CMake_config.png) 20 | A few adjustments are recommend: 21 | * On the top of the window, The user may want to 22 | enable `Reload CMake project on editing CMakeLists.txt or other Cmake configuration files` for the convenience of automatically reloading after adding or removing source files." 23 | * The user may want to add more cmake profiles(One may do so by pressing the `+` button or with hot 24 | key `Alt Insert`) for different toolchains or build types. 25 | 4. Build, run, and debug the project\ 26 | After opening the project, CLion loads CMakeLists.txt(`cmake -B cmake-build-debug` or paths set). 27 | * ![build.png](./build.png): Build(`cmake --build $CMAKE_BUILD_PATH`, hot key:`Ctrl F9` by default) 28 | * ![run.png](./run.png): Build and Run(hot key: `Shift F10`) 29 | * ![debug.png](./debug.png): Build and Debug(hot key: `Shift F9) 30 | -------------------------------------------------------------------------------- /.github/docs/CLionQuickStart/CMake_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/.github/docs/CLionQuickStart/CMake_config.png -------------------------------------------------------------------------------- /.github/docs/CLionQuickStart/GH_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/.github/docs/CLionQuickStart/GH_list.png -------------------------------------------------------------------------------- /.github/docs/CLionQuickStart/VCS_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/.github/docs/CLionQuickStart/VCS_menu.png -------------------------------------------------------------------------------- /.github/docs/CLionQuickStart/build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/.github/docs/CLionQuickStart/build.png -------------------------------------------------------------------------------- /.github/docs/CLionQuickStart/debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/.github/docs/CLionQuickStart/debug.png -------------------------------------------------------------------------------- /.github/docs/CLionQuickStart/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/.github/docs/CLionQuickStart/img.png -------------------------------------------------------------------------------- /.github/docs/CLionQuickStart/mainUI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/.github/docs/CLionQuickStart/mainUI.png -------------------------------------------------------------------------------- /.github/docs/CLionQuickStart/project_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/.github/docs/CLionQuickStart/project_menu.png -------------------------------------------------------------------------------- /.github/docs/CLionQuickStart/run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/.github/docs/CLionQuickStart/run.png -------------------------------------------------------------------------------- /.github/docs/Code-Styles-and-Guidelines.md: -------------------------------------------------------------------------------- 1 | # Code Styles and Guidelines 2 | 3 | ## General 4 | 5 | * Use UTF-8 encoding 6 | * Windows should use CRLF line breaks, MacOS and Linux(Unix-like OSes) should use LF line breaks 7 | * Use `enum class` over `enum` 8 | https://stackoverflow.com/questions/18335861/why-is-enum-class-preferred-over-plain-enum 9 | https://www.geeksforgeeks.org/enum-classes-in-c-and-their-advantage-over-enum-datatype/ 10 | * Line break at EOF 11 | https://stackoverflow.com/questions/729692/why-should-text-files-end-with-a-newline 12 | Force it in VSCode 13 | https://stackoverflow.com/questions/44704968/visual-studio-code-insert-newline-at-the-end-of-files 14 | * Write portable code by following C++ standards and avoid using `#pragma` directives 15 | * Add line breaks wherever it makes sense 16 | * Use primary operators instead of alternative ones([ref](https://en.cppreference.com/w/cpp/language/operator_alternative)) 17 | 18 | ## Includes 19 | 20 | Use `#ifndef` for include guards instead of `#pragma once` 21 | 22 | Include guard format: `__` 23 | 24 | E.g.: Include guard for `GameObject.hpp` should be `GAME_OBJECT_HPP` 25 | 26 | Internal header should use `""` and external headers should use `<>` 27 | 28 | Unless specified, included headers should be the following order: 29 | 30 | 1. definition header(if needed) 31 | 2. system header 32 | 3. external header 33 | 4. internal header 34 | 35 | Different categories are separated with line breaks. If headers are in the same category, headers for the same libraries should be grouped together and be in alphabetical order 36 | 37 | E.g. includes for `Player.cpp` should be 38 | 39 | ```cpp= 40 | #include "Player.hpp" 41 | 42 | #include 43 | #include 44 | 45 | #include 46 | 47 | #include "Math/Vector2.hpp" 48 | 49 | // ... 50 | ``` 51 | 52 | See also: https://clangd.llvm.org/guides/include-cleaner 53 | 54 | ## Class 55 | 56 | Member functions and variables declaration should be in the following order, access specifiers should be in the order of `public` -> `protected` -> `private`. Static members should be declared above normal members. 57 | 58 | 1. Functions 59 | 1. Constructor 60 | 1. Default 61 | 2. Parameterized 62 | 3. Copy 63 | 4. Move 64 | 2. Destructor 65 | 3. Operator overload 66 | 1. Copy assignment 67 | 2. Move assignment 68 | 4. Getter 69 | 5. Setter 70 | 6. Other 71 | 2. Variables 72 | 73 | Follow C++ rule of three/five/zero 74 | https://en.cppreference.com/w/cpp/language/rule_of_three 75 | 76 | ## File Types 77 | 78 | C source file: `.c` 79 | C header file: `.h` 80 | C++ source file: `.cpp` 81 | C++ header file: `.hpp` 82 | 83 | Vertex Shader: `.vert` 84 | Fragment Shader: `.frag` 85 | 86 | ## File and Folder Naming 87 | 88 | Source and header files should be `PascalCase` if it defines a class or struct, otherwise it should be `snake_case` 89 | 90 | Top level folders should only consist of a single word and be `lowercase` (e.g. `src/`, `include/`, `lib/`) 91 | 92 | Source and header file folders should be `PascalCase` and be the same name as its namespace 93 | 94 | E.g.: `include/Math/Vector2.hpp` should be: 95 | 96 | ```cpp= 97 | #ifndef MATH_VECTOR2_HPP 98 | #define MATH_VECTOR2_HPP 99 | 100 | namespace Math { 101 | class Vector2 { 102 | public: 103 | Vector2(); 104 | 105 | } 106 | } 107 | 108 | #endif 109 | 110 | ``` 111 | 112 | ## Variable Naming Convention 113 | 114 | Lower items override higher items if rules collide 115 | 116 | | | Case | Prefix | Suffix | 117 | | ------------------- | ------------ | ------ | ------ | 118 | | Namespace | `PascalCase` | | | 119 | | Class | `PascalCase` | | | 120 | | Class member | `PascalCase` | `m_` | | 121 | | Static class member | `PascalCase` | `s_` | | 122 | | Struct | `PascalCase` | | | 123 | | Struct member | `camelCase` | | | 124 | | Enum | `PascalCase` | | | 125 | | Enum element | `UPPER_CASE` | | | 126 | | Function/Method | `camelCase` | | | 127 | | Parameter | `camelCase` | | | 128 | | Variable | `camelCase` | | | 129 | | Type Alias/Typedef | `PascalCase` | | | 130 | | Global constant | `UPPER_CASE` | | | 131 | | Macro | `UPPER_CASE` | | | 132 | | Template parameter | `UPPER_CASE` | | | 133 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | os: [ubuntu-latest] 16 | compiler: [{ cc: gcc, cxx: g++ }, { cc: clang, cxx: clang++ }] 17 | build_type: [Release] 18 | binary: [linux-binary] 19 | include: 20 | - os: windows-latest 21 | build_type: Release 22 | binary: windows-binary 23 | - os: macos-latest 24 | build_type: Release 25 | 26 | steps: 27 | - if: matrix.os == 'ubuntu-latest' 28 | name: Installing Dependencies 29 | run: | 30 | sudo apt-get update 31 | sudo apt-get -y install ${{ matrix.compiler.cc }} cmake ninja-build libglu1-mesa-dev mesa-common-dev xorg-dev 32 | 33 | - name: Checkout Repository 34 | uses: actions/checkout@v4 35 | with: 36 | submodules: recursive 37 | 38 | - name: Building Project 39 | if: matrix.os == 'ubuntu-latest' 40 | env: 41 | CC: ${{ matrix.compiler.cc }} 42 | CXX: ${{ matrix.compiler.cxx }} 43 | run: | 44 | cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} 45 | cmake --build build 46 | 47 | - name: Building Project 48 | if: matrix.os == 'windows-latest' 49 | run: | 50 | cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} 51 | cmake --build build --config ${{ matrix.build_type }} 52 | 53 | - name: Building Project 54 | if: matrix.os == 'macos-latest' 55 | run: | 56 | cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} 57 | cmake --build build 58 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: check 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout Repository 14 | uses: actions/checkout@v4 15 | with: 16 | submodules: recursive 17 | 18 | - name: Check Format 19 | uses: jidicula/clang-format-action@v4.11.0 20 | with: 21 | clang-format-version: '17' 22 | check-path: src include test assets/shaders 23 | include-regex: ^.*\.((((c|C)(c|pp|xx|\+\+)?$)|((h|H)h?(pp|xx|\+\+)?$))|(vert|frag))$ 24 | 25 | - name: Run Linter 26 | uses: ZedThree/clang-tidy-review@v0.21.0 27 | with: 28 | apt_packages: ninja-build,libglu1-mesa-dev,mesa-common-dev,xorg-dev 29 | cmake_command: cmake -B build -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=on 30 | build_dir: build 31 | exclude: lib 32 | split_workflow: true 33 | config_file: .clang-tidy 34 | 35 | - name: Upload Result 36 | uses: ZedThree/clang-tidy-review/upload@v0.21.0 37 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - uses: mattnotmitt/doxygen-action@v1.9.5 17 | with: 18 | doxyfile-path: docs/Doxyfile 19 | 20 | - uses: peaceiris/actions-gh-pages@v3.9.3 21 | with: 22 | github_token: ${{ secrets.GITHUB_TOKEN }} 23 | publish_dir: ./docs/html 24 | -------------------------------------------------------------------------------- /.github/workflows/post.yml: -------------------------------------------------------------------------------- 1 | name: post 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["check"] 6 | types: 7 | - completed 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: ZedThree/clang-tidy-review/post@v0.14.0 15 | with: 16 | annotations: false 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore file for CLion, Visual Studio, and Visual Studio Code 2 | 3 | # From JetBrains .gitignore template 4 | # https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 5 | 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # Ignore .DS_Store file from a git repository 10 | 11 | .DS_Store 12 | 13 | # User-specific stuff 14 | .idea/**/workspace.xml 15 | .idea/**/tasks.xml 16 | .idea/**/usage.statistics.xml 17 | .idea/**/dictionaries 18 | .idea/**/shelf 19 | 20 | # AWS User-specific 21 | .idea/**/aws.xml 22 | 23 | # Generated files 24 | .idea/**/contentModel.xml 25 | 26 | # Sensitive or high-churn files 27 | .idea/**/dataSources/ 28 | .idea/**/dataSources.ids 29 | .idea/**/dataSources.local.xml 30 | .idea/**/sqlDataSources.xml 31 | .idea/**/dynamic.xml 32 | .idea/**/uiDesigner.xml 33 | .idea/**/dbnavigator.xml 34 | 35 | # Gradle 36 | .idea/**/gradle.xml 37 | .idea/**/libraries 38 | 39 | # Gradle and Maven with auto-import 40 | # When using Gradle or Maven with auto-import, you should exclude module files, 41 | # since they will be recreated, and may cause churn. Uncomment if using 42 | # auto-import. 43 | # .idea/artifacts 44 | # .idea/compiler.xml 45 | # .idea/jarRepositories.xml 46 | # .idea/modules.xml 47 | # .idea/*.iml 48 | # .idea/modules 49 | # *.iml 50 | # *.ipr 51 | 52 | # CMake 53 | cmake-build-*/ 54 | 55 | # Mongo Explorer plugin 56 | .idea/**/mongoSettings.xml 57 | 58 | # File-based project format 59 | *.iws 60 | 61 | # IntelliJ 62 | out/ 63 | 64 | # mpeltonen/sbt-idea plugin 65 | .idea_modules/ 66 | 67 | # JIRA plugin 68 | atlassian-ide-plugin.xml 69 | 70 | # Cursive Clojure plugin 71 | .idea/replstate.xml 72 | 73 | # SonarLint plugin 74 | .idea/sonarlint/ 75 | 76 | # Crashlytics plugin (for Android Studio and IntelliJ) 77 | com_crashlytics_export_strings.xml 78 | crashlytics.properties 79 | crashlytics-build.properties 80 | fabric.properties 81 | 82 | # Editor-based Rest Client 83 | .idea/httpRequests 84 | 85 | # Android studio 3.1+ serialized cache file 86 | .idea/caches/build_file_checksums.ser 87 | 88 | # From Visual Studio .gitignore template 89 | # https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 90 | 91 | ## Ignore Visual Studio temporary files, build results, and 92 | ## files generated by popular Visual Studio add-ons. 93 | ## 94 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 95 | 96 | # User-specific files 97 | *.rsuser 98 | *.suo 99 | *.user 100 | *.userosscache 101 | *.sln.docstates 102 | 103 | # User-specific files (MonoDevelop/Xamarin Studio) 104 | *.userprefs 105 | 106 | # Mono auto generated files 107 | mono_crash.* 108 | 109 | # Build results 110 | [Dd]ebug/ 111 | [Dd]ebugPublic/ 112 | [Rr]elease/ 113 | [Rr]eleases/ 114 | x64/ 115 | x86/ 116 | [Ww][Ii][Nn]32/ 117 | [Aa][Rr][Mm]/ 118 | [Aa][Rr][Mm]64/ 119 | bld/ 120 | [Bb]in/ 121 | [Oo]bj/ 122 | [Ll]og/ 123 | [Ll]ogs/ 124 | 125 | # Visual Studio 2015/2017 cache/options directory 126 | .vs/ 127 | # Uncomment if you have tasks that create the project's static files in wwwroot 128 | #wwwroot/ 129 | 130 | # Visual Studio 2017 auto generated files 131 | Generated\ Files/ 132 | 133 | # MSTest test Results 134 | [Tt]est[Rr]esult*/ 135 | [Bb]uild[Ll]og.* 136 | 137 | # NUnit 138 | *.VisualState.xml 139 | TestResult.xml 140 | nunit-*.xml 141 | 142 | # Build Results of an ATL Project 143 | [Dd]ebugPS/ 144 | [Rr]eleasePS/ 145 | dlldata.c 146 | 147 | # Benchmark Results 148 | BenchmarkDotNet.Artifacts/ 149 | 150 | # .NET Core 151 | project.lock.json 152 | project.fragment.lock.json 153 | artifacts/ 154 | 155 | # ASP.NET Scaffolding 156 | ScaffoldingReadMe.txt 157 | 158 | # StyleCop 159 | StyleCopReport.xml 160 | 161 | # Files built by Visual Studio 162 | *_i.c 163 | *_p.c 164 | *_h.h 165 | *.ilk 166 | *.meta 167 | *.obj 168 | *.iobj 169 | *.pch 170 | *.pdb 171 | *.ipdb 172 | *.pgc 173 | *.pgd 174 | *.rsp 175 | *.sbr 176 | *.tlb 177 | *.tli 178 | *.tlh 179 | *.tmp 180 | *.tmp_proj 181 | *_wpftmp.csproj 182 | *.log 183 | *.tlog 184 | *.vspscc 185 | *.vssscc 186 | .builds 187 | *.pidb 188 | *.svclog 189 | *.scc 190 | 191 | # Chutzpah Test files 192 | _Chutzpah* 193 | 194 | # Visual C++ cache files 195 | ipch/ 196 | *.aps 197 | *.ncb 198 | *.opendb 199 | *.opensdf 200 | *.sdf 201 | *.cachefile 202 | *.VC.db 203 | *.VC.VC.opendb 204 | 205 | # Visual Studio profiler 206 | *.psess 207 | *.vsp 208 | *.vspx 209 | *.sap 210 | 211 | # Visual Studio Trace Files 212 | *.e2e 213 | 214 | # TFS 2012 Local Workspace 215 | $tf/ 216 | 217 | # Guidance Automation Toolkit 218 | *.gpState 219 | 220 | # ReSharper is a .NET coding add-in 221 | _ReSharper*/ 222 | *.[Rr]e[Ss]harper 223 | *.DotSettings.user 224 | 225 | # TeamCity is a build add-in 226 | _TeamCity* 227 | 228 | # DotCover is a Code Coverage Tool 229 | *.dotCover 230 | 231 | # AxoCover is a Code Coverage Tool 232 | .axoCover/* 233 | !.axoCover/settings.json 234 | 235 | # Coverlet is a free, cross platform Code Coverage Tool 236 | coverage*.json 237 | coverage*.xml 238 | coverage*.info 239 | 240 | # Visual Studio code coverage results 241 | *.coverage 242 | *.coveragexml 243 | 244 | # NCrunch 245 | _NCrunch_* 246 | .*crunch*.local.xml 247 | nCrunchTemp_* 248 | 249 | # MightyMoose 250 | *.mm.* 251 | AutoTest.Net/ 252 | 253 | # Web workbench (sass) 254 | .sass-cache/ 255 | 256 | # Installshield output folder 257 | [Ee]xpress/ 258 | 259 | # DocProject is a documentation generator add-in 260 | DocProject/buildhelp/ 261 | DocProject/Help/*.HxT 262 | DocProject/Help/*.HxC 263 | DocProject/Help/*.hhc 264 | DocProject/Help/*.hhk 265 | DocProject/Help/*.hhp 266 | DocProject/Help/Html2 267 | DocProject/Help/html 268 | 269 | # Click-Once directory 270 | publish/ 271 | 272 | # Publish Web Output 273 | *.[Pp]ublish.xml 274 | *.azurePubxml 275 | # Note: Comment the next line if you want to checkin your web deploy settings, 276 | # but database connection strings (with potential passwords) will be unencrypted 277 | *.pubxml 278 | *.publishproj 279 | 280 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 281 | # checkin your Azure Web App publish settings, but sensitive information contained 282 | # in these scripts will be unencrypted 283 | PublishScripts/ 284 | 285 | # NuGet Packages 286 | *.nupkg 287 | # NuGet Symbol Packages 288 | *.snupkg 289 | # The packages folder can be ignored because of Package Restore 290 | **/[Pp]ackages/* 291 | # except build/, which is used as an MSBuild target. 292 | !**/[Pp]ackages/build/ 293 | # Uncomment if necessary however generally it will be regenerated when needed 294 | #!**/[Pp]ackages/repositories.config 295 | # NuGet v3's project.json files produces more ignorable files 296 | *.nuget.props 297 | *.nuget.targets 298 | 299 | # Microsoft Azure Build Output 300 | csx/ 301 | *.build.csdef 302 | 303 | # Microsoft Azure Emulator 304 | ecf/ 305 | rcf/ 306 | 307 | # Windows Store app package directories and files 308 | AppPackages/ 309 | BundleArtifacts/ 310 | Package.StoreAssociation.xml 311 | _pkginfo.txt 312 | *.appx 313 | *.appxbundle 314 | *.appxupload 315 | 316 | # Visual Studio cache files 317 | # files ending in .cache can be ignored 318 | *.[Cc]ache 319 | # but keep track of directories ending in .cache 320 | !?*.[Cc]ache/ 321 | 322 | # Others 323 | ClientBin/ 324 | ~$* 325 | *~ 326 | *.dbmdl 327 | *.dbproj.schemaview 328 | *.jfm 329 | *.pfx 330 | *.publishsettings 331 | orleans.codegen.cs 332 | 333 | # Including strong name files can present a security risk 334 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 335 | #*.snk 336 | 337 | # Since there are multiple workflows, uncomment next line to ignore bower_components 338 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 339 | #bower_components/ 340 | 341 | # RIA/Silverlight projects 342 | Generated_Code/ 343 | 344 | # Backup & report files from converting an old project file 345 | # to a newer Visual Studio version. Backup files are not needed, 346 | # because we have git ;-) 347 | _UpgradeReport_Files/ 348 | Backup*/ 349 | UpgradeLog*.XML 350 | UpgradeLog*.htm 351 | ServiceFabricBackup/ 352 | *.rptproj.bak 353 | 354 | # SQL Server files 355 | *.mdf 356 | *.ldf 357 | *.ndf 358 | 359 | # Business Intelligence projects 360 | *.rdl.data 361 | *.bim.layout 362 | *.bim_*.settings 363 | *.rptproj.rsuser 364 | *- [Bb]ackup.rdl 365 | *- [Bb]ackup ([0-9]).rdl 366 | *- [Bb]ackup ([0-9][0-9]).rdl 367 | 368 | # Microsoft Fakes 369 | FakesAssemblies/ 370 | 371 | # GhostDoc plugin setting file 372 | *.GhostDoc.xml 373 | 374 | # Node.js Tools for Visual Studio 375 | .ntvs_analysis.dat 376 | node_modules/ 377 | 378 | # Visual Studio 6 build log 379 | *.plg 380 | 381 | # Visual Studio 6 workspace options file 382 | *.opt 383 | 384 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 385 | *.vbw 386 | 387 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 388 | *.vbp 389 | 390 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 391 | *.dsw 392 | *.dsp 393 | 394 | # Visual Studio 6 technical files 395 | *.ncb 396 | *.aps 397 | 398 | # Visual Studio LightSwitch build output 399 | **/*.HTMLClient/GeneratedArtifacts 400 | **/*.DesktopClient/GeneratedArtifacts 401 | **/*.DesktopClient/ModelManifest.xml 402 | **/*.Server/GeneratedArtifacts 403 | **/*.Server/ModelManifest.xml 404 | _Pvt_Extensions 405 | 406 | # Paket dependency manager 407 | .paket/paket.exe 408 | paket-files/ 409 | 410 | # FAKE - F# Make 411 | .fake/ 412 | 413 | # CodeRush personal settings 414 | .cr/personal 415 | 416 | # Python Tools for Visual Studio (PTVS) 417 | __pycache__/ 418 | *.pyc 419 | 420 | # Cake - Uncomment if you are using it 421 | # tools/** 422 | # !tools/packages.config 423 | 424 | # Tabs Studio 425 | *.tss 426 | 427 | # Telerik's JustMock configuration file 428 | *.jmconfig 429 | 430 | # BizTalk build output 431 | *.btp.cs 432 | *.btm.cs 433 | *.odx.cs 434 | *.xsd.cs 435 | 436 | # OpenCover UI analysis results 437 | OpenCover/ 438 | 439 | # Azure Stream Analytics local run output 440 | ASALocalRun/ 441 | 442 | # MSBuild Binary and Structured Log 443 | *.binlog 444 | 445 | # NVidia Nsight GPU debugger configuration file 446 | *.nvuser 447 | 448 | # MFractors (Xamarin productivity tool) working folder 449 | .mfractor/ 450 | 451 | # Local History for Visual Studio 452 | .localhistory/ 453 | 454 | # Visual Studio History (VSHistory) files 455 | .vshistory/ 456 | 457 | # BeatPulse healthcheck temp database 458 | healthchecksdb 459 | 460 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 461 | MigrationBackup/ 462 | 463 | # Ionide (cross platform F# VS Code tools) working folder 464 | .ionide/ 465 | 466 | # Fody - auto-generated XML schema 467 | FodyWeavers.xsd 468 | 469 | # VS Code files for those working on multiple tools 470 | # .vscode/* 471 | # !.vscode/settings.json 472 | # !.vscode/tasks.json 473 | # !.vscode/launch.json 474 | # !.vscode/extensions.json 475 | # *.code-workspace 476 | 477 | # Local History for Visual Studio Code 478 | .history/ 479 | 480 | # Windows Installer files from build outputs 481 | *.cab 482 | *.msi 483 | *.msix 484 | *.msm 485 | *.msp 486 | 487 | # JetBrains 488 | *.sln.iml 489 | /.idea/ 490 | # Custom writen VSCode .gitignore 491 | .vscode/ 492 | 493 | # Default build directory for CMake extension 494 | build/ 495 | 496 | # Ignore 3rd party dependencies 497 | lib/ 498 | 499 | # Doxygen generated files 500 | docs/html/ 501 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(PracticalToolsForSimpleDesign) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | set(CMAKE_CXX_EXTENSIONS OFF) 7 | if (MSVC) 8 | set(TARGET_COMPILE_OPTIONS 9 | /W4 10 | ) 11 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/build) 12 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/build) 13 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/build) 14 | else() 15 | set(TARGET_COMPILE_OPTIONS 16 | -Wall -Wextra -pedantic 17 | ) 18 | endif() 19 | 20 | if (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 21 | # see https://github.com/ntut-open-source-club/practical-tools-for-simple-design/issues/22 22 | set(CMAKE_RC_FLAGS="-C 1252") 23 | endif() 24 | 25 | option(PTSD_ENABLE_PCH "Turn on PCH to increase compilation speed" OFF) 26 | 27 | include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Dependencies.cmake) 28 | 29 | set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) 30 | set(SRC_FILES 31 | ${SRC_DIR}/config.cpp 32 | 33 | ${SRC_DIR}/Core/Context.cpp 34 | ${SRC_DIR}/Core/DebugMessageCallback.cpp 35 | ${SRC_DIR}/Core/VertexArray.cpp 36 | ${SRC_DIR}/Core/VertexBuffer.cpp 37 | ${SRC_DIR}/Core/IndexBuffer.cpp 38 | ${SRC_DIR}/Core/Shader.cpp 39 | ${SRC_DIR}/Core/Program.cpp 40 | ${SRC_DIR}/Core/Texture.cpp 41 | ${SRC_DIR}/Core/TextureUtils.cpp 42 | 43 | ${SRC_DIR}/Util/LoadTextFile.cpp 44 | ${SRC_DIR}/Util/Logger.cpp 45 | ${SRC_DIR}/Util/Time.cpp 46 | ${SRC_DIR}/Util/Input.cpp 47 | ${SRC_DIR}/Util/SFX.cpp 48 | ${SRC_DIR}/Util/BGM.cpp 49 | ${SRC_DIR}/Util/Image.cpp 50 | ${SRC_DIR}/Util/Text.cpp 51 | ${SRC_DIR}/Util/TransformUtils.cpp 52 | ${SRC_DIR}/Util/GameObject.cpp 53 | ${SRC_DIR}/Util/Renderer.cpp 54 | ${SRC_DIR}/Util/Color.cpp 55 | ${SRC_DIR}/Util/Animation.cpp 56 | ${SRC_DIR}/Util/MissingTexture.cpp 57 | ${SRC_DIR}/Util/Position.cpp 58 | ) 59 | set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) 60 | set(INCLUDE_FILES 61 | ${INCLUDE_DIR}/pch.hpp 62 | ${INCLUDE_DIR}/config.hpp 63 | 64 | ${INCLUDE_DIR}/Core/Context.hpp 65 | ${INCLUDE_DIR}/Core/DebugMessageCallback.hpp 66 | ${INCLUDE_DIR}/Core/VertexArray.hpp 67 | ${INCLUDE_DIR}/Core/VertexBuffer.hpp 68 | ${INCLUDE_DIR}/Core/UniformBuffer.hpp 69 | ${INCLUDE_DIR}/Core/UniformBuffer.inl 70 | ${INCLUDE_DIR}/Core/IndexBuffer.hpp 71 | ${INCLUDE_DIR}/Core/Shader.hpp 72 | ${INCLUDE_DIR}/Core/Program.hpp 73 | ${INCLUDE_DIR}/Core/Texture.hpp 74 | ${INCLUDE_DIR}/Core/TextureUtils.hpp 75 | ${INCLUDE_DIR}/Core/Drawable.hpp 76 | ${INCLUDE_DIR}/Core/MissingFontTextureBase64.hpp 77 | ${INCLUDE_DIR}/Core/MissingImageTextureBase64.hpp 78 | 79 | ${INCLUDE_DIR}/Util/LoadTextFile.hpp 80 | ${INCLUDE_DIR}/Util/Logger.hpp 81 | ${INCLUDE_DIR}/Util/Time.hpp 82 | ${INCLUDE_DIR}/Util/Input.hpp 83 | ${INCLUDE_DIR}/Util/Keycode.hpp 84 | ${INCLUDE_DIR}/Util/SFX.hpp 85 | ${INCLUDE_DIR}/Util/BGM.hpp 86 | ${INCLUDE_DIR}/Util/Image.hpp 87 | ${INCLUDE_DIR}/Util/Text.hpp 88 | ${INCLUDE_DIR}/Util/Transform.hpp 89 | ${INCLUDE_DIR}/Util/TransformUtils.hpp 90 | ${INCLUDE_DIR}/Util/GameObject.hpp 91 | ${INCLUDE_DIR}/Util/Renderer.hpp 92 | ${INCLUDE_DIR}/Util/Color.hpp 93 | ${INCLUDE_DIR}/Util/MissingTexture.hpp 94 | ${INCLUDE_DIR}/Util/Base64.hpp 95 | ${INCLUDE_DIR}/Util/Animation.hpp 96 | ${INCLUDE_DIR}/Util/Position.hpp 97 | ) 98 | set(EXAMPLE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/example) 99 | set(EXAMPLE_FILES 100 | ${EXAMPLE_DIR}/src/App.cpp 101 | ${EXAMPLE_DIR}/src/Giraffe.cpp 102 | ${EXAMPLE_DIR}/src/GiraffeText.cpp 103 | ${EXAMPLE_DIR}/src/Cat.cpp 104 | ${EXAMPLE_DIR}/src/main.cpp 105 | 106 | ${EXAMPLE_DIR}/include/App.hpp 107 | ${EXAMPLE_DIR}/include/Giraffe.hpp 108 | ${EXAMPLE_DIR}/include/GiraffeText.hpp 109 | ${EXAMPLE_DIR}/include/Cat.hpp 110 | ) 111 | set(TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test) 112 | set(TEST_FILES 113 | ${TEST_DIR}/SimpleTest.cpp 114 | ${TEST_DIR}/NotSimpleTest.cpp 115 | ${TEST_DIR}/TransformTest.cpp 116 | ) 117 | 118 | add_library(PTSD STATIC 119 | ${SRC_FILES} 120 | ${INCLUDE_FILES} 121 | ) 122 | target_link_libraries(PTSD 123 | ${DEPENDENCY_LINK_LIBRARIES} 124 | ) 125 | target_include_directories(PTSD SYSTEM PRIVATE 126 | ${DEPENDENCY_INCLUDE_DIRS} 127 | ) 128 | target_include_directories(PTSD PRIVATE 129 | ${INCLUDE_DIR} 130 | ) 131 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 132 | target_compile_definitions(PTSD PRIVATE PTSD_ASSETS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/assets") 133 | else() 134 | # TODO 135 | message(AUTHOR_WARNING "relative PTSD_ASSETS_DIR is WIP, Please use `-DCMAKE_BUILD_TYPE=Debug` build for now.") 136 | target_compile_definitions(PTSD PRIVATE PTSD_ASSETS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/assets") 137 | endif() 138 | if (${PTSD_ENABLE_PCH}) 139 | target_precompile_headers(PTSD PRIVATE 140 | include/pch.hpp 141 | ) 142 | endif() 143 | 144 | target_compile_options(PTSD PRIVATE 145 | ${TARGET_COMPILE_OPTIONS} 146 | ) 147 | 148 | if(NOT PROJECT_IS_TOP_LEVEL) 149 | set(DEPENDENCY_INCLUDE_DIRS ${DEPENDENCY_INCLUDE_DIRS} PARENT_SCOPE) 150 | set(DEPENDENCY_LINK_LIBRARIES ${DEPENDENCY_LINK_LIBRARIES} PARENT_SCOPE) 151 | endif() 152 | 153 | add_executable(Example EXCLUDE_FROM_ALL 154 | ${EXAMPLE_FILES} 155 | ) 156 | target_link_libraries(Example 157 | SDL2::SDL2main # IDK what happens if we don't link SDL2main 158 | PTSD 159 | ) 160 | target_include_directories(Example SYSTEM PRIVATE 161 | ${INCLUDE_DIR} 162 | ${DEPENDENCY_INCLUDE_DIRS} 163 | ) 164 | target_include_directories(Example PRIVATE 165 | ${EXAMPLE_DIR}/include 166 | ) 167 | 168 | target_compile_definitions(Example PRIVATE ASSETS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/assets") 169 | 170 | enable_testing() 171 | 172 | add_executable(Tests EXCLUDE_FROM_ALL 173 | ${TEST_FILES} 174 | ) 175 | target_link_libraries(Tests 176 | PTSD 177 | GTest::gtest_main 178 | ) 179 | target_include_directories(Tests SYSTEM PRIVATE 180 | ${INCLUDE_DIR} 181 | ${DEPENDENCY_INCLUDE_DIRS} 182 | lib/googletest/googletest/include 183 | lib/googletest/googlemock/include 184 | ) 185 | target_compile_options(Tests PRIVATE 186 | ${TARGET_COMPILE_OPTIONS} 187 | ) 188 | 189 | add_executable(AudioTest EXCLUDE_FROM_ALL 190 | ${TEST_DIR}/Interactive/Audio.cpp 191 | ) 192 | target_link_libraries(AudioTest 193 | PTSD 194 | GTest::gtest_main 195 | ) 196 | target_include_directories(AudioTest SYSTEM PRIVATE 197 | ${INCLUDE_DIR} 198 | ${DEPENDENCY_INCLUDE_DIRS} 199 | lib/googletest/googletest/include 200 | lib/googletest/googlemock/include 201 | ) 202 | target_compile_options(AudioTest PRIVATE 203 | ${TARGET_COMPILE_OPTIONS} 204 | ) 205 | 206 | include(GoogleTest) 207 | gtest_discover_tests(Tests) 208 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | - [Code Styles and Guidelines](./.github/docs/Code-Styles-and-Guidelines.md) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 NTUT Open-Source Club 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Practical Tools for Simple Design 2 | 3 | ###### Officially Supported Platforms and IDEs 4 | 5 | | | Windows | macOS | Linux | 6 | |:------------------------------:|:-------:|:-----:|:-----:| 7 | | CLion | V | V | V | 8 | | VSCode[^codeoss] | V | V | V | 9 | | Visual Studio[^novs][^vsmacos] | ? | X | X | 10 | | No IDE | V | V | V | 11 | 12 | [^vsmacos]: [Microsoft Kills Visual Studio for Mac](https://visualstudiomagazine.com/articles/2023/08/30/vs-for-mac-retirement.aspx) 13 | [^codeoss]: On Linux the support of Code - OSS and VSCodium aren't guaranteed. 14 | [^novs]: Due to lack of testing there may or may not be more issues on VS. Anyway, building PTSD on VS is available. 15 | 16 | ## Getting Started 17 | 18 | Required: Git, CMake, C/C++ Compiler, OpenGL Implementation 19 | 20 | Optional: Ninja Build, Clang 21 | 22 | > You might get some issue like https://github.com/ntut-open-source-club/practical-tools-for-simple-design/issues/78 check it if you need. 23 | 24 | ### Command Line 25 | 26 | [//]: # (TODO: No IDE Quick Start) 27 | > [!WARNING] 28 | > This section is work in progress. 29 | 30 | ``` 31 | git clone https://github.com/ntut-open-source-club/practical-tools-for-simple-design.git 32 | cd practical-tools-for-simple-design 33 | cmake -B build 34 | cmake --build build 35 | ``` 36 | 37 | > If Ninja Build is install use `cmake -B build -G Ninja` to speed compile time 38 | 39 | > For older versions of CMake(`<3.13`? verification needed) use 40 | > ``` 41 | > mkdir build 42 | > cd build 43 | > cmake . 44 | > cmake --build . 45 | > ``` 46 | > if the `-B` flag is unsupported 47 | 48 | > If using Neovim or other LSP supported editors, append `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` to the generation command for `clangd` to work 49 | 50 | ### VSCode 51 | 52 | [//]: # (TODO: VSCode Quick Start) 53 | > [!WARNING] 54 | > This section is work in progress. 55 | 56 | ### CLion 57 | 58 | [CLion Quick Start](.github/docs/CLionQuickStart/CLionQuickStart.md) 59 | 60 | ###### NOTE: If you have time, read [OOP2023f Environment Setup](https://hackmd.io/@OOP2023f/rk2-8cVCh) 61 | 62 | ## Generate Doxygen Documents 63 | 64 | Required: Doxygen 1.9.6 65 | 66 | ``` 67 | doxygen docs/Doxyfile 68 | ``` 69 | 70 | Open the generated documents with your favorite browser at `docs/html/index.html` 71 | -------------------------------------------------------------------------------- /assets/audio/Click.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/assets/audio/Click.wav -------------------------------------------------------------------------------- /assets/audio/testbgm.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/assets/audio/testbgm.mp3 -------------------------------------------------------------------------------- /assets/fonts/Inter.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/assets/fonts/Inter.ttf -------------------------------------------------------------------------------- /assets/icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/assets/icon.jpg -------------------------------------------------------------------------------- /assets/shaders/Base.frag: -------------------------------------------------------------------------------- 1 | #version 410 core 2 | 3 | layout(location = 0) in vec2 uv; 4 | 5 | layout(location = 0) out vec4 fragColor; 6 | 7 | uniform sampler2D surface; 8 | 9 | void main() { 10 | vec4 texColor = texture(surface, uv); 11 | 12 | if (texColor.a < 0.01) 13 | discard; 14 | 15 | fragColor = texColor; 16 | } 17 | -------------------------------------------------------------------------------- /assets/shaders/Base.vert: -------------------------------------------------------------------------------- 1 | #version 410 core 2 | 3 | layout(location = 0) in vec2 vertPosition; 4 | layout(location = 1) in vec2 vertUv; 5 | 6 | layout(location = 0) out vec2 uv; 7 | 8 | layout(std140) uniform Matrices { 9 | mat4 model; 10 | mat4 viewProjection; 11 | }; 12 | 13 | void main() { 14 | // Reference from 15 | // https://github.com/NOOBDY/Indigo/blob/f31c7ef82c610d8e91214892a7a1e3f860ba4aaa/assets/shaders/base_pass.vert#L21-L22 16 | gl_Position = viewProjection * model * vec4(vertPosition, 0, 1); 17 | 18 | uv = vertUv; 19 | } 20 | -------------------------------------------------------------------------------- /assets/shaders/Triangle.frag: -------------------------------------------------------------------------------- 1 | #version 410 core 2 | 3 | layout(location = 0) in vec3 vertexColor; 4 | 5 | layout(location = 0) out vec4 fragColor; 6 | 7 | void main() { 8 | fragColor = vec4(vertexColor, 1.0); 9 | // fragColor.a *= 0.5; 10 | } 11 | -------------------------------------------------------------------------------- /assets/shaders/Triangle.vert: -------------------------------------------------------------------------------- 1 | #version 410 core 2 | 3 | layout(location = 0) in vec2 vertexPos; 4 | layout(location = 1) in vec3 color; 5 | 6 | layout(location = 0) out vec3 vertexColor; 7 | 8 | /** 9 | * C++ and GLSL maps `mat2` differently because weird padding issues 10 | * https://www.khronos.org/opengl/wiki/Layout_Qualifier_(GLSL)#Interface_components 11 | * 12 | * Sample code: `mat2 m = {a, b, c, d};` 13 | * 14 | * C++: 15 | * | a | b | c | d | 16 | * 17 | * GLSL: 18 | * | a | b | | | 19 | * | c | d | | | 20 | */ 21 | layout(std140) uniform Triangle { 22 | vec4 _model; 23 | vec4 _projection; 24 | }; 25 | 26 | void main() { 27 | mat2 model = mat2(_model); 28 | mat2 projection = mat2(_projection); 29 | 30 | vec2 pos = vertexPos * model * projection; 31 | 32 | gl_Position = vec4(pos.x, pos.y, 0, 1); 33 | vertexColor = color; 34 | } 35 | -------------------------------------------------------------------------------- /assets/sprites/cat/cat-0.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/assets/sprites/cat/cat-0.bmp -------------------------------------------------------------------------------- /assets/sprites/cat/cat-1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/assets/sprites/cat/cat-1.bmp -------------------------------------------------------------------------------- /assets/sprites/cat/cat-2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/assets/sprites/cat/cat-2.bmp -------------------------------------------------------------------------------- /assets/sprites/cat/cat-3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/assets/sprites/cat/cat-3.bmp -------------------------------------------------------------------------------- /assets/sprites/cat/cat-4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/assets/sprites/cat/cat-4.bmp -------------------------------------------------------------------------------- /assets/sprites/cat/cat-5.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/assets/sprites/cat/cat-5.bmp -------------------------------------------------------------------------------- /assets/sprites/cat/cat-6.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/assets/sprites/cat/cat-6.bmp -------------------------------------------------------------------------------- /assets/sprites/cat/cat-7.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/assets/sprites/cat/cat-7.bmp -------------------------------------------------------------------------------- /assets/sprites/giraffe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntut-open-source-club/practical-tools-for-simple-design/28669352a5bd8466a8d0b34de816c2935c3dfab5/assets/sprites/giraffe.png -------------------------------------------------------------------------------- /cmake/Dependencies.cmake: -------------------------------------------------------------------------------- 1 | find_package(OpenGL REQUIRED) 2 | 3 | cmake_policy(SET CMP0135 NEW) 4 | 5 | include(FetchContent) 6 | set(FETCH_CONTENT_QUIET FALSE) 7 | 8 | FetchContent_Declare( 9 | glew 10 | 11 | # URL https://github.com/nigels-com/glew/releases/download/glew-2.2.0/glew-2.2.0.zip 12 | # URL_HASH MD5=970535b75b1b69ebd018a0fa05af63d1 13 | GIT_REPOSITORY https://github.com/Perlmint/glew-cmake.git 14 | GIT_TAG 918ece3da858c2e28e10f6507378af01647cb139 15 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/glew 16 | ) 17 | 18 | FetchContent_Declare( 19 | sdl2 20 | 21 | URL https://github.com/libsdl-org/SDL/releases/download/release-2.32.4/SDL2-2.32.4.zip 22 | # URL_HASH MD5=ccda8e75aa61112bc541eeb78d13784d 23 | # GIT_REPOSITORY https://github.com/lidsdl-org/SDL.git 24 | # GIT_TAG 2359383fc187386204c3bb22de89655a494cd128 25 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/sdl2 26 | ) 27 | 28 | FetchContent_Declare( 29 | sdl2_image 30 | 31 | URL https://github.com/libsdl-org/SDL_image/releases/download/release-2.6.3/SDL2_image-2.6.3.zip 32 | URL_HASH MD5=ecedb5078bbd31e7d1552e2b1443d2f6 33 | # GIT_REPOSITORY https://github.com/libsdl-org/SDL_image.git 34 | # GIT_TAG c1bf2245b0ba63a25afe2f8574d305feca25af77 35 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/sdl2_image 36 | ) 37 | 38 | FetchContent_Declare( 39 | sdl2_ttf 40 | 41 | # URL https://github.com/libsdl-org/SDL_ttf/releases/download/release-3.2.2/SDL3_ttf-3.2.2.zip 42 | # URL_HASH MD5=e4ddd51fa4825f26b5c9bc8f4b010ed1 43 | GIT_REPOSITORY https://github.com/libsdl-org/SDL_ttf.git 44 | GIT_TAG db1e6d14494bfffe464c50667a26941cc3a36324 45 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/sdl2_ttf 46 | ) 47 | FetchContent_Declare( 48 | sdl2_mixer 49 | 50 | URL https://github.com/libsdl-org/SDL_mixer/releases/download/release-2.6.3/SDL2_mixer-2.6.3.zip 51 | URL_HASH MD5=fb3e71ef072ff8dd793cec3ed384f9a0 52 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/sdl2_mixer 53 | ) 54 | 55 | FetchContent_Declare( # At this time 1.11.0 has some issues formatting `const unsigned char *` 56 | spdlog 57 | 58 | URL https://github.com/gabime/spdlog/archive/refs/tags/v1.10.0.zip 59 | URL_HASH MD5=031565384b28f29e44c6e7fb247ad48a 60 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/spdlog 61 | ) 62 | 63 | FetchContent_Declare( 64 | glm 65 | 66 | URL https://github.com/g-truc/glm/releases/download/1.0.1/glm-1.0.1-light.7z 67 | # URL_HASH MD5=7d235d4813a2e7b1e10cc711b8e25213 68 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/glm 69 | ) 70 | 71 | FetchContent_Declare( 72 | googletest 73 | 74 | URL https://github.com/google/googletest/archive/refs/tags/v1.13.0.zip 75 | URL_HASH MD5=a1279c6fb5bf7d4a5e0d0b2a4adb39ac 76 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/googletest 77 | ) 78 | 79 | FetchContent_Declare( 80 | imgui 81 | 82 | URL https://github.com/ocornut/imgui/archive/refs/tags/v1.90.4-docking.zip 83 | URL_HASH MD5=384084df566474aec3729df4ea30b937 84 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/imgui 85 | ) 86 | 87 | FetchContent_Declare( 88 | nlohmann_json 89 | 90 | URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz 91 | URL_HASH MD5=c23a33f04786d85c29fda8d16b5f0efd 92 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/nlohmann_json 93 | ) 94 | 95 | set(BUILD_SHARED_LIBS FALSE) 96 | 97 | set(SDL2IMAGE_INSTALL OFF) 98 | set(SDL2IMAGE_VENDORED ON) 99 | 100 | set(SDL2TTF_INSTALL OFF) 101 | set(SDL2TTF_VENDORED ON) 102 | 103 | set(SDL2MIXER_INSTALL OFF) 104 | set(SDL2MIXER_VENDORED ON) 105 | set(SDL2MIXER_FLAC OFF) 106 | set(SDL2MIXER_MIDI OFF) 107 | set(SDL2MIXER_MOD OFF) 108 | set(SDL2MIXER_OPUS OFF) 109 | set(SDL2MIXER_OGG OFF) 110 | set(SDL2MIXER_VORBIS OFF) 111 | set(SDL2MIXER_VOC OFF) 112 | 113 | set(JSON_ImplicitConversions OFF) 114 | 115 | # For Windows: Prevent overriding the parent project's compiler/linker settings 116 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 117 | 118 | add_compile_definitions(GLEW_NO_GLU) 119 | 120 | FetchContent_MakeAvailable(glew sdl2 sdl2_image sdl2_ttf sdl2_mixer spdlog glm googletest imgui nlohmann_json) 121 | 122 | set(IMGUI_SOURCE 123 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/imgui/backends/imgui_impl_sdl2.cpp 124 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/imgui/backends/imgui_impl_opengl3.cpp 125 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/imgui/imgui.cpp 126 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/imgui/imgui_demo.cpp 127 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/imgui/imgui_draw.cpp 128 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/imgui/imgui_tables.cpp 129 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/imgui/imgui_widgets.cpp 130 | ) 131 | 132 | set(IMGUI_INCLUDE_DIR 133 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/imgui/ 134 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/imgui/backends/ 135 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/sdl2/include/ 136 | ) 137 | 138 | add_library(ImGui STATIC 139 | ${IMGUI_SOURCE} 140 | ) 141 | target_include_directories(ImGui PUBLIC 142 | ${IMGUI_INCLUDE_DIR} 143 | ) 144 | 145 | 146 | set(DEPENDENCY_LINK_LIBRARIES 147 | ${OPENGL_LIBRARY} 148 | libglew_static 149 | 150 | SDL2::SDL2-static 151 | SDL2_image::SDL2_image-static 152 | SDL2_ttf::SDL2_ttf-static 153 | SDL2_mixer::SDL2_mixer-static 154 | 155 | spdlog::spdlog 156 | 157 | ImGui 158 | 159 | nlohmann_json 160 | ) 161 | 162 | set(DEPENDENCY_INCLUDE_DIRS 163 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/sdl2/include/ 164 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/glew/include/ 165 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/spdlog/include/ 166 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/ 167 | ${IMGUI_INCLUDE_DIR} 168 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/nlohmann_json/include/ 169 | ) 170 | -------------------------------------------------------------------------------- /docs/custom.css: -------------------------------------------------------------------------------- 1 | div#nav-tree ul.children_ul ul.children_ul ul.children_ul a[class^="namespace"]::before { 2 | content: "N "; 3 | /* from .icon */ 4 | font-family: Arial, Helvetica; 5 | font-weight: bold; 6 | font-size: 12px; 7 | height: 14px; 8 | width: 16px; 9 | display: inline-block; 10 | background-color: #0081FA; 11 | color: white; 12 | text-align: center; 13 | border-radius: 4px; 14 | margin-left: 2px; 15 | margin-right: 6px; 16 | } 17 | -------------------------------------------------------------------------------- /docs/delete_me.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/doxygen-awesome/doxygen-awesome-darkmode-toggle.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2021 - 2023 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | class DoxygenAwesomeDarkModeToggle extends HTMLElement { 31 | // SVG icons from https://fonts.google.com/icons 32 | // Licensed under the Apache 2.0 license: 33 | // https://www.apache.org/licenses/LICENSE-2.0.html 34 | static lightModeIcon = `` 35 | static darkModeIcon = `` 36 | static title = "Toggle Light/Dark Mode" 37 | 38 | static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode" 39 | static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode" 40 | 41 | static _staticConstructor = function() { 42 | DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference) 43 | // Update the color scheme when the browsers preference changes 44 | // without user interaction on the website. 45 | window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { 46 | DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged() 47 | }) 48 | // Update the color scheme when the tab is made visible again. 49 | // It is possible that the appearance was changed in another tab 50 | // while this tab was in the background. 51 | document.addEventListener("visibilitychange", visibilityState => { 52 | if (document.visibilityState === 'visible') { 53 | DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged() 54 | } 55 | }); 56 | }() 57 | 58 | static init() { 59 | $(function() { 60 | $(document).ready(function() { 61 | const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle') 62 | toggleButton.title = DoxygenAwesomeDarkModeToggle.title 63 | toggleButton.updateIcon() 64 | 65 | window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { 66 | toggleButton.updateIcon() 67 | }) 68 | document.addEventListener("visibilitychange", visibilityState => { 69 | if (document.visibilityState === 'visible') { 70 | toggleButton.updateIcon() 71 | } 72 | }); 73 | 74 | $(document).ready(function(){ 75 | document.getElementById("MSearchBox").parentNode.appendChild(toggleButton) 76 | }) 77 | $(window).resize(function(){ 78 | document.getElementById("MSearchBox").parentNode.appendChild(toggleButton) 79 | }) 80 | }) 81 | }) 82 | } 83 | 84 | constructor() { 85 | super(); 86 | this.onclick=this.toggleDarkMode 87 | } 88 | 89 | /** 90 | * @returns `true` for dark-mode, `false` for light-mode system preference 91 | */ 92 | static get systemPreference() { 93 | return window.matchMedia('(prefers-color-scheme: dark)').matches 94 | } 95 | 96 | /** 97 | * @returns `true` for dark-mode, `false` for light-mode user preference 98 | */ 99 | static get userPreference() { 100 | return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) || 101 | (DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)) 102 | } 103 | 104 | static set userPreference(userPreference) { 105 | DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference 106 | if(!userPreference) { 107 | if(DoxygenAwesomeDarkModeToggle.systemPreference) { 108 | localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true) 109 | } else { 110 | localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey) 111 | } 112 | } else { 113 | if(!DoxygenAwesomeDarkModeToggle.systemPreference) { 114 | localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true) 115 | } else { 116 | localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey) 117 | } 118 | } 119 | DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged() 120 | } 121 | 122 | static enableDarkMode(enable) { 123 | if(enable) { 124 | DoxygenAwesomeDarkModeToggle.darkModeEnabled = true 125 | document.documentElement.classList.add("dark-mode") 126 | document.documentElement.classList.remove("light-mode") 127 | } else { 128 | DoxygenAwesomeDarkModeToggle.darkModeEnabled = false 129 | document.documentElement.classList.remove("dark-mode") 130 | document.documentElement.classList.add("light-mode") 131 | } 132 | } 133 | 134 | static onSystemPreferenceChanged() { 135 | DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference 136 | DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled) 137 | } 138 | 139 | static onUserPreferenceChanged() { 140 | DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled) 141 | } 142 | 143 | toggleDarkMode() { 144 | DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference 145 | this.updateIcon() 146 | } 147 | 148 | updateIcon() { 149 | if(DoxygenAwesomeDarkModeToggle.darkModeEnabled) { 150 | this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon 151 | } else { 152 | this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon 153 | } 154 | } 155 | } 156 | 157 | customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle); 158 | -------------------------------------------------------------------------------- /docs/doxygen-awesome/doxygen-awesome-sidebar-only-darkmode-toggle.css: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | 4 | Doxygen Awesome 5 | https://github.com/jothepro/doxygen-awesome-css 6 | 7 | MIT License 8 | 9 | Copyright (c) 2021 - 2023 jothepro 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | 29 | */ 30 | 31 | @media screen and (min-width: 768px) { 32 | 33 | #MSearchBox { 34 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px); 35 | } 36 | 37 | #MSearchField { 38 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /docs/doxygen-awesome/doxygen-awesome-sidebar-only.css: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2021 - 2023 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | html { 31 | /* side nav width. MUST be = `TREEVIEW_WIDTH`. 32 | * Make sure it is wide enough to contain the page title (logo + title + version) 33 | */ 34 | --side-nav-fixed-width: 335px; 35 | --menu-display: none; 36 | 37 | --top-height: 120px; 38 | --toc-sticky-top: -25px; 39 | --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px); 40 | } 41 | 42 | #projectname { 43 | white-space: nowrap; 44 | } 45 | 46 | 47 | @media screen and (min-width: 768px) { 48 | html { 49 | --searchbar-background: var(--page-background-color); 50 | } 51 | 52 | #side-nav { 53 | min-width: var(--side-nav-fixed-width); 54 | max-width: var(--side-nav-fixed-width); 55 | top: var(--top-height); 56 | overflow: visible; 57 | } 58 | 59 | #nav-tree, #side-nav { 60 | height: calc(100vh - var(--top-height)) !important; 61 | } 62 | 63 | #nav-tree { 64 | padding: 0; 65 | } 66 | 67 | #top { 68 | display: block; 69 | border-bottom: none; 70 | height: var(--top-height); 71 | margin-bottom: calc(0px - var(--top-height)); 72 | max-width: var(--side-nav-fixed-width); 73 | overflow: hidden; 74 | background: var(--side-nav-background); 75 | } 76 | #main-nav { 77 | float: left; 78 | padding-right: 0; 79 | } 80 | 81 | .ui-resizable-handle { 82 | cursor: default; 83 | width: 1px !important; 84 | box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color); 85 | } 86 | 87 | #nav-path { 88 | position: fixed; 89 | right: 0; 90 | left: var(--side-nav-fixed-width); 91 | bottom: 0; 92 | width: auto; 93 | } 94 | 95 | #doc-content { 96 | height: calc(100vh - 31px) !important; 97 | padding-bottom: calc(3 * var(--spacing-large)); 98 | padding-top: calc(var(--top-height) - 80px); 99 | box-sizing: border-box; 100 | margin-left: var(--side-nav-fixed-width) !important; 101 | } 102 | 103 | #MSearchBox { 104 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium))); 105 | } 106 | 107 | #MSearchField { 108 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px); 109 | } 110 | 111 | #MSearchResultsWindow { 112 | left: var(--spacing-medium) !important; 113 | right: auto; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /docs/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | $projectname: $title 10 | $title 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | $treeview 20 | $search 21 | $mathjax 22 | $darkmode 23 | 24 | $extrastylesheet 25 | 26 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 37 |
38 | 39 | 40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 53 | 54 | 55 | 56 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |
49 |
$projectname $projectnumber 50 |
51 |
$projectbrief
52 |
57 |
$projectbrief
58 |
$searchbox
$searchbox
76 |
77 | 78 | 79 | -------------------------------------------------------------------------------- /example/include/App.hpp: -------------------------------------------------------------------------------- 1 | #ifndef APP_HPP 2 | #define APP_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | #include "Util/Renderer.hpp" 7 | 8 | #include "Cat.hpp" 9 | #include "Giraffe.hpp" 10 | #include "GiraffeText.hpp" 11 | 12 | class App { 13 | public: 14 | enum class State { 15 | START, 16 | UPDATE, 17 | END, 18 | }; 19 | 20 | State GetCurrentState() const { return m_CurrentState; } 21 | 22 | void Start(); 23 | void Update(); 24 | void End(); // NOLINT(readability-convert-member-functions-to-static) 25 | 26 | private: 27 | State m_CurrentState = State::START; 28 | 29 | std::shared_ptr m_Giraffe = std::make_shared(); 30 | // std::shared_ptr m_GiraffeText = 31 | // std::make_shared(); 32 | 33 | std::shared_ptr m_Cat = std::make_shared(); 34 | Util::Renderer m_Root; 35 | 36 | bool showDemoWindow = true; 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /example/include/Cat.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CAT_HPP 2 | #define CAT_HPP 3 | 4 | #include "pch.hpp" 5 | 6 | #include "Util/Animation.hpp" 7 | #include "Util/GameObject.hpp" 8 | 9 | class Cat : public Util::GameObject { 10 | public: 11 | Cat(); 12 | 13 | void Update(); 14 | 15 | private: 16 | std::shared_ptr m_Animation; 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /example/include/Giraffe.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GIRAFFE_HPP 2 | #define GIRAFFE_HPP 3 | 4 | #include 5 | 6 | #include "GiraffeText.hpp" 7 | #include "Util/GameObject.hpp" 8 | #include "Util/Text.hpp" 9 | 10 | class Giraffe : public Util::GameObject { 11 | 12 | public: 13 | void Update(); 14 | 15 | void Start(); 16 | 17 | private: 18 | std::shared_ptr m_GiraffeText; 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /example/include/GiraffeText.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GIRAFFE_TEXT_HPP 2 | #define GIRAFFE_TEXT_HPP 3 | 4 | #include "Util/GameObject.hpp" 5 | #include "Util/Text.hpp" 6 | 7 | class GiraffeText : public Util::GameObject { 8 | public: 9 | GiraffeText() = default; 10 | 11 | GiraffeText(std::string font, const int size) 12 | : m_Font(std::move(font)), 13 | m_Size(size) {} 14 | 15 | ~GiraffeText() override = default; 16 | 17 | void Start(); 18 | 19 | void Update(); 20 | 21 | private: 22 | std::string m_Font; 23 | int m_Size; 24 | std::shared_ptr m_Text; 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /example/src/App.cpp: -------------------------------------------------------------------------------- 1 | #include "App.hpp" 2 | 3 | #include "Util/Image.hpp" 4 | #include "Util/Input.hpp" 5 | #include "Util/Keycode.hpp" 6 | #include "Util/Logger.hpp" 7 | 8 | #include "GiraffeText.hpp" 9 | 10 | void App::Start() { 11 | LOG_TRACE("Start"); 12 | 13 | m_Giraffe->SetDrawable( 14 | std::make_shared("../assets/sprites/giraffe.png")); 15 | m_Giraffe->SetZIndex(5); 16 | m_Giraffe->Start(); 17 | 18 | m_Root.AddChild(m_Giraffe); 19 | m_Root.AddChild(m_Cat); 20 | 21 | m_CurrentState = State::UPDATE; 22 | } 23 | 24 | void App::Update() { 25 | if (Util::Input::IsKeyPressed(Util::Keycode::MOUSE_LB)) { 26 | LOG_DEBUG("Left button pressed"); 27 | } 28 | if (Util::Input::IsKeyDown(Util::Keycode::MOUSE_RB)) { 29 | LOG_DEBUG("Right button down"); 30 | } 31 | if (Util::Input::IsKeyUp(Util::Keycode::MOUSE_RB)) { 32 | LOG_DEBUG("Right button up"); 33 | } 34 | if (Util::Input::IfScroll()) { 35 | auto delta = Util::Input::GetScrollDistance(); 36 | LOG_DEBUG("Scrolling: x: {}, y: {}", delta.x, delta.y); 37 | } 38 | if (Util::Input::IsMouseMoving()) { 39 | // LOG_DEBUG("Mouse moving! x:{}, y{}", cursorPos.x, cursorPos.y); 40 | } 41 | 42 | if (Util::Input::IsKeyUp(Util::Keycode::ESCAPE) || Util::Input::IfExit()) { 43 | m_CurrentState = State::END; 44 | } 45 | 46 | if (Util::Input::IsKeyDown(Util::Keycode::A)) { 47 | LOG_DEBUG("A Down"); 48 | } 49 | 50 | if (Util::Input::IsKeyPressed(Util::Keycode::B)) { 51 | LOG_DEBUG("B Pressed. Setting the cursor to (0, 0)."); 52 | Util::Input::SetCursorPosition({0.0F, 0.0F}); 53 | LOG_DEBUG("Cursor set to {}.", Util::Input::GetCursorPosition()); 54 | } 55 | 56 | m_Giraffe->Update(); 57 | m_Cat->Update(); 58 | 59 | m_Root.Update(); 60 | 61 | // press SPACE to toggle demo window 62 | if (Util::Input::IsKeyDown(Util::Keycode::SPACE)) { 63 | showDemoWindow = !showDemoWindow; 64 | } 65 | if (showDemoWindow) { 66 | ImGui::ShowDemoWindow(); 67 | } 68 | } 69 | 70 | void App::End() { // NOLINT(this method will mutate members in the future) 71 | LOG_TRACE("End"); 72 | } 73 | -------------------------------------------------------------------------------- /example/src/Cat.cpp: -------------------------------------------------------------------------------- 1 | #include "Cat.hpp" 2 | 3 | #include "Util/Input.hpp" 4 | #include "Util/Logger.hpp" 5 | 6 | Cat::Cat() 7 | : m_Animation(std::make_shared( 8 | std::vector{ 9 | "../assets/sprites/cat/cat-0.bmp", 10 | "../assets/sprites/cat/cat-1.bmp", 11 | "../assets/sprites/cat/cat-2.bmp", 12 | "../assets/sprites/cat/cat-3.bmp", 13 | "../assets/sprites/cat/cat-4.bmp", 14 | "../assets/sprites/cat/cat-5.bmp", 15 | "../assets/sprites/cat/cat-6.bmp", 16 | "../assets/sprites/cat/cat-7.bmp", 17 | }, 18 | true, 50, true, 1000)) { 19 | m_Transform.translation = {-200, 200}; 20 | SetDrawable(m_Animation); 21 | } 22 | 23 | void Cat::Update() { 24 | if (Util::Input::IsKeyDown(Util::Keycode::A)) { 25 | LOG_DEBUG("Pause Animate"); 26 | m_Animation->Pause(); 27 | } 28 | 29 | if (Util::Input::IsKeyDown(Util::Keycode::S)) { 30 | LOG_DEBUG("Play Animate"); 31 | m_Animation->Play(); 32 | } 33 | if (Util::Input::IsKeyDown(Util::Keycode::V)) { 34 | LOG_DEBUG("Set Visible to true"); 35 | SetVisible(true); 36 | } 37 | if (Util::Input::IsKeyDown(Util::Keycode::B)) { 38 | LOG_DEBUG("Set Visible to false"); 39 | SetVisible(false); 40 | } 41 | 42 | LOG_TRACE(m_Animation->GetCurrentFrameIndex()); 43 | 44 | if (Util::Input::IsKeyDown(Util::Keycode::D)) { 45 | m_Animation->SetLooping(false); 46 | } 47 | 48 | if (Util::Input::IsKeyDown(Util::Keycode::F)) { 49 | m_Animation->SetLooping(true); 50 | } 51 | 52 | if (Util::Input::IsKeyUp(Util::Keycode::K)) { 53 | m_Animation->SetInterval(50); 54 | } 55 | if (Util::Input::IsKeyDown(Util::Keycode::K)) { 56 | m_Animation->SetInterval(500); 57 | } 58 | if (Util::Input::IsKeyDown(Util::Keycode::O)) { 59 | m_Animation->SetCurrentFrame(5); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /example/src/Giraffe.cpp: -------------------------------------------------------------------------------- 1 | #include "Giraffe.hpp" 2 | 3 | #include 4 | 5 | #include "Util/Time.hpp" 6 | #include "Util/Transform.hpp" 7 | 8 | #include "config.hpp" 9 | 10 | void Giraffe::Start() { 11 | m_GiraffeText = 12 | std::make_shared("../assets/fonts/Inter.ttf", 50); 13 | m_GiraffeText->SetZIndex(this->GetZIndex() - 1); 14 | m_GiraffeText->Start(); 15 | this->AddChild(m_GiraffeText); 16 | } 17 | 18 | void Giraffe::Update() { 19 | static glm::vec2 dir = {1, 0.5}; 20 | 21 | auto &pos = m_Transform.translation; 22 | auto &scale = m_Transform.scale; 23 | auto &rotation = m_Transform.rotation; 24 | 25 | if (pos.y >= static_cast(PTSD_Config::WINDOW_HEIGHT) / 2 || 26 | pos.y + static_cast(PTSD_Config::WINDOW_HEIGHT) / 2 <= 0) { 27 | dir.y *= -1; 28 | } 29 | if (pos.x >= static_cast(PTSD_Config::WINDOW_WIDTH) / 2 || 30 | pos.x + static_cast(PTSD_Config::WINDOW_WIDTH) / 2 <= 0) { 31 | dir.x *= -1; 32 | } 33 | 34 | // sonarcloud called it redundant, but ms_t = float is just a coincidence. 35 | auto delta = static_cast(Util::Time::GetDeltaTimeMs()); 36 | Util::Transform deltaTransform{ 37 | dir * delta, 0.002F * delta, 38 | glm::vec2(1, 1) * (std::sin(rotation / 2) + 1.0F) * 100.0F}; 39 | 40 | pos += deltaTransform.translation; 41 | rotation += deltaTransform.rotation; 42 | 43 | m_GiraffeText->Update(); 44 | } 45 | -------------------------------------------------------------------------------- /example/src/GiraffeText.cpp: -------------------------------------------------------------------------------- 1 | #include "GiraffeText.hpp" 2 | 3 | #include "Util/Color.hpp" 4 | #include "Util/Time.hpp" 5 | 6 | void GiraffeText::Start() { 7 | m_Text = std::make_unique(m_Font, m_Size, "0", 8 | Util::Color::FromRGB(255, 255, 255)); 9 | SetDrawable(m_Text); 10 | } 11 | 12 | void GiraffeText::Update() { 13 | ImGui::Begin("Giraffe Text"); 14 | ImGui::SetWindowSize({300, 100}); 15 | ImGui::DragFloat2("Translation", &m_Transform.translation[0], 1, -100, 100); 16 | ImGui::DragFloat("Rotation", &m_Transform.rotation, 0.01F, 0, 17 | 2 * glm::pi()); 18 | ImGui::DragFloat2("Scale", &m_Transform.scale[0], 0.1F, 1, 10); 19 | ImGui::End(); 20 | m_Text->SetText( 21 | fmt::format("{:.02f}", 1000.0F / Util::Time::GetDeltaTimeMs())); 22 | 23 | m_Text->SetColor(Util::Color::FromName(Util::Colors::RED)); 24 | } 25 | -------------------------------------------------------------------------------- /example/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "App.hpp" 2 | 3 | #include "Core/Context.hpp" 4 | 5 | #include "Util/Input.hpp" 6 | 7 | int main(int, char **) { 8 | auto context = Core::Context::GetInstance(); 9 | App app; 10 | 11 | // set icon in window. 12 | context->SetWindowIcon(ASSETS_DIR "/icon.jpg"); 13 | 14 | while (!context->GetExit()) { 15 | context->Setup(); 16 | 17 | switch (app.GetCurrentState()) { 18 | case App::State::START: 19 | app.Start(); 20 | break; 21 | 22 | case App::State::UPDATE: 23 | app.Update(); 24 | break; 25 | 26 | case App::State::END: 27 | app.End(); 28 | context->SetExit(true); 29 | break; 30 | } 31 | 32 | ImGui::Render(); 33 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 34 | context->Update(); 35 | } 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /include/Core/Context.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CORE_CONTEXT_HPP 2 | #define CORE_CONTEXT_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | #include "config.hpp" 7 | 8 | #include "Util/Time.hpp" 9 | 10 | namespace Core { 11 | class Context { 12 | public: 13 | /** 14 | * @brief Initialize context for SDL, OpenGL, and create a window 15 | */ 16 | Context(); 17 | Context(const Context &) = delete; 18 | Context(Context &&) = delete; 19 | 20 | ~Context(); 21 | 22 | Context &operator=(const Context &) = delete; 23 | Context &operator=(Context &&) = delete; 24 | 25 | static std::shared_ptr GetInstance(); 26 | 27 | bool GetExit() const { return m_Exit; } 28 | unsigned int GetWindowWidth() const { return m_WindowWidth; } 29 | unsigned int GetWindowHeight() const { return m_WindowHeight; } 30 | 31 | void SetExit(bool exit) { m_Exit = exit; } 32 | void SetWindowWidth(unsigned int width) { m_WindowWidth = width; } 33 | void SetWindowHeight(unsigned int height) { m_WindowHeight = height; } 34 | void SetWindowIcon(const std::string &path); 35 | 36 | void Setup(); 37 | void Update(); 38 | 39 | private: 40 | SDL_Window *m_Window; 41 | SDL_GLContext m_GlContext; 42 | 43 | static std::shared_ptr s_Instance; 44 | bool m_Exit = false; 45 | 46 | unsigned int m_WindowWidth = PTSD_Config::WINDOW_WIDTH; 47 | unsigned int m_WindowHeight = PTSD_Config::WINDOW_HEIGHT; 48 | 49 | // Can't access Time::s_Now, so using this variable to track time. 50 | Util::ms_t m_BeforeUpdateTime = Util::Time::GetElapsedTimeMs(); 51 | }; 52 | } // namespace Core 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/Core/DebugMessageCallback.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CORE_DEBUG_MESSAGE_CALLBACK_HPP 2 | #define CORE_DEBUG_MESSAGE_CALLBACK_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | namespace Core { 7 | /** 8 | * @brief Callback function for OpenGL handling 9 | * 10 | * This function is called by OpenGL, if users want to use it they should do 11 | * so via `glDebugMessageInsert()` and not call it directly 12 | * 13 | * @see https://www.khronos.org/opengl/wiki/Debug_Output 14 | */ 15 | void GLAPIENTRY OpenGLDebugMessageCallback(GLenum source, GLenum type, 16 | GLuint id, // NOLINT 17 | GLenum severity, GLsizei length, 18 | const GLchar *message, 19 | const void *data); 20 | } // namespace Core 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/Core/Docs.hpp: -------------------------------------------------------------------------------- 1 | #error This file is only for documentation purposes only 2 | 3 | /** 4 | * @brief %Core functionality of the framework 5 | */ 6 | namespace Core {} 7 | -------------------------------------------------------------------------------- /include/Core/Drawable.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CORE_DRAWABLE_HPP 2 | #define CORE_DRAWABLE_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | #include "Util/Transform.hpp" 7 | 8 | namespace Core { 9 | struct Matrices { 10 | glm::mat4 m_Model; 11 | glm::mat4 m_Projection; 12 | }; 13 | 14 | class Drawable { 15 | public: 16 | virtual ~Drawable() = default; 17 | virtual void Draw(const Core::Matrices &data) = 0; 18 | virtual glm::vec2 GetSize() const = 0; 19 | }; 20 | } // namespace Core 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/Core/IndexBuffer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CORE_INDEX_BUFFER_HPP 2 | #define CORE_INDEX_BUFFER_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | namespace Core { 7 | /** 8 | * @brief Wrapper for OpenGL Index Buffer Object (a.k.a 9 | * `GL_ELEMENT_ARRAY_BUFFER`) 10 | */ 11 | class IndexBuffer { 12 | public: 13 | explicit IndexBuffer(const std::vector &indices); 14 | IndexBuffer(const IndexBuffer &) = delete; 15 | IndexBuffer(IndexBuffer &&other); 16 | 17 | ~IndexBuffer(); 18 | 19 | IndexBuffer &operator=(const IndexBuffer &) = delete; 20 | IndexBuffer &operator=(IndexBuffer &&other); 21 | 22 | size_t GetCount() const { return m_Count; } 23 | 24 | void Bind() const; 25 | void Unbind() const; 26 | 27 | private: 28 | GLuint m_BufferId; 29 | 30 | size_t m_Count; 31 | }; 32 | } // namespace Core 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /include/Core/MissingImageTextureBase64.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CORE_MISSING_IMAGE_TEXTURE_BASE64_HPP 2 | #define CORE_MISSING_IMAGE_TEXTURE_BASE64_HPP 3 | 4 | // A transparent image base64 string. 5 | // Since we want to hardcode the image, we have such a long string here. 6 | // The original image should find in here: https://i.imgur.com/zS4sPCN.png 7 | static constexpr const char *MISSING_IMAGE_TEXTURE = 8 | "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEABAMAAACuXLVVAAAAIGNIUk0AAHomAACAhAAA+" 9 | "gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAhUExURf8A3P8A3uEAwh4AGgAAAP8A3+" 10 | "IAwx0AGcoArjUALv///" 11 | "xhkxvUAAAABYktHRApo0PRWAAAAB3RJTUUH5wwZEiAsl4pL0QAAAQlJREFUeNrt2rENAjEMBVC" 12 | "vcGzArRA2IBIbsAESA9CwACuwLrVDnfiK5/" 13 | "YX9xRdY+tH5DldeprreW7eAwAAAAAAAAAAAAAAAACgHLDlub9yfnvMzXvseZ7flub9mZu3aGs/" 14 | "OOZ79LVPPubbCJj90415AAAAAAAAAAAAAAAAABwOsH45LV/" 15 | "Pyw8U5SeaWPvTHfBKBgAAAAAAAAAAAAAAAFC+nJav5+" 16 | "UHir72yfUHAAAAAAAAAAAAAAAAAPQH9Af0BwAAAAAAAAAAAAAAAAD0B/" 17 | "QH9AcAAAAAAAAAAAAAAAAA9Af0B/" 18 | "QHAAAAAAAAAAAAAAAAAPQH9Af0BwAAAAAAAAAAAAAAAAAA/gA/" 19 | "YLVKK0nwuR8AAAAldEVYdGRhdGU6Y3JlYXRlADIwMjMtMTItMjVUMTg6MzI6MzcrMDA6MDCbYQ" 20 | "aeAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIzLTEyLTI1VDE4OjMyOjM3KzAwOjAw6jy+" 21 | "IgAAACh0RVh0ZGF0ZTp0aW1lc3RhbXAAMjAyMy0xMi0yNVQxODozMjo0NCswMDowMIYEjHkAAA" 22 | "AASUVORK5CYII="; 23 | 24 | #endif // CORE_MISSING_IMAGE_TEXTURE_BASE64_HPP 25 | -------------------------------------------------------------------------------- /include/Core/Program.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CORE_PROGRAM_HPP 2 | #define CORE_PROGRAM_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | namespace Core { 7 | /** 8 | * In OpenGL, programs are objects composed of multiple shaders files compiled 9 | * and linked together. A typical program would require at least a vertex shader 10 | * and a fragment shader. However, users could add more optional middle layers 11 | * such as geometry shaders or tesselation shaders. 12 | */ 13 | class Program { 14 | public: 15 | Program(const std::string &vertexShaderFilepath, 16 | const std::string &fragmentShaderFilepath); 17 | Program(const Program &) = delete; 18 | Program(Program &&other); 19 | 20 | ~Program(); 21 | 22 | Program &operator=(const Program &) = delete; 23 | Program &operator=(Program &&other); 24 | 25 | GLuint GetId() const { return m_ProgramId; } 26 | 27 | void Bind() const; 28 | void Unbind() const; 29 | 30 | void Validate() const; 31 | 32 | private: 33 | void CheckStatus() const; 34 | 35 | GLuint m_ProgramId; 36 | }; 37 | } // namespace Core 38 | #endif 39 | -------------------------------------------------------------------------------- /include/Core/Shader.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CORE_SHADER_HPP 2 | #define CORE_SHADER_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | namespace Core { 7 | /** 8 | * @see Core::Program 9 | */ 10 | class Shader { 11 | public: 12 | enum class Type { 13 | VERTEX = GL_VERTEX_SHADER, 14 | FRAGMENT = GL_FRAGMENT_SHADER, 15 | }; 16 | 17 | Shader(const std::string &filepath, Type shaderType); 18 | Shader(const Shader &) = delete; 19 | Shader(Shader &&other); 20 | 21 | ~Shader(); 22 | 23 | Shader &operator=(const Shader &) = delete; 24 | Shader &operator=(Shader &&other); 25 | 26 | GLuint GetShaderId() const { return m_ShaderId; } 27 | 28 | private: 29 | void Compile(const std::string &src) const; 30 | void CheckStatus(const std::string &filepath) const; 31 | 32 | GLuint m_ShaderId; 33 | }; 34 | } // namespace Core 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/Core/Texture.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CORE_TEXTURE_HPP 2 | #define CORE_TEXTURE_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | namespace Core { 7 | class Texture { 8 | public: 9 | Texture(GLint format, int width, int height, const void *data, bool useAA); 10 | Texture(const Texture &) = delete; 11 | Texture(Texture &&texture); 12 | 13 | ~Texture(); 14 | 15 | Texture &operator=(const Texture &) = delete; 16 | Texture &operator=(Texture &&other); 17 | 18 | GLuint GetTextureId() const { return m_TextureId; } 19 | 20 | void Bind(int slot) const; 21 | void Unbind() const; 22 | 23 | void UpdateData(GLint format, int width, int height, const void *data); 24 | void UseAntiAliasing(bool useAA); 25 | 26 | private: 27 | GLuint m_TextureId; 28 | 29 | GLenum m_MinFilter; 30 | GLenum m_MagFilter; 31 | }; 32 | } // namespace Core 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /include/Core/TextureUtils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CORE_TEXTURE_UTILS_HPP 2 | #define CORE_TEXTURE_UTILS_HPP 3 | 4 | #include "pch.hpp" 5 | 6 | #include "Util/Logger.hpp" 7 | 8 | namespace Core { 9 | GLint SdlFormatToGlFormat(Uint32 format); 10 | 11 | GLint GlFormatToGlInternalFormat(GLint format); 12 | } // namespace Core 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /include/Core/UniformBuffer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CORE_UNIFORM_BUFFER_HPP 2 | #define CORE_UNIFORM_BUFFER_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | #include "Core/Program.hpp" 7 | 8 | namespace Core { 9 | /** 10 | * @brief Wrapper for OpenGL Uniform Buffer Object (a.k.a `GL_UNIFORM_BUFFER`) 11 | * 12 | * This class uses templates because data is passed in as `void *` in the 13 | * original C function, using templates would provide more robust type checking 14 | * and no need for manually entering the type size. 15 | * 16 | * @see https://www.khronos.org/opengl/wiki/Uniform_Buffer_Object 17 | */ 18 | template 19 | class UniformBuffer { 20 | public: 21 | UniformBuffer(const Program &program, const std::string &name, int binding); 22 | UniformBuffer(const UniformBuffer &) = delete; 23 | UniformBuffer(UniformBuffer &&other); 24 | 25 | ~UniformBuffer(); 26 | 27 | UniformBuffer &operator=(const UniformBuffer &) = delete; 28 | UniformBuffer &operator=(UniformBuffer &&other); 29 | 30 | void SetData(int offset, const T &data); 31 | 32 | private: 33 | GLuint m_Binding; 34 | GLuint m_BufferId; 35 | }; 36 | } // namespace Core 37 | 38 | #include "UniformBuffer.inl" 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /include/Core/UniformBuffer.inl: -------------------------------------------------------------------------------- 1 | #include "UniformBuffer.hpp" 2 | 3 | namespace Core { 4 | template 5 | UniformBuffer::UniformBuffer(const Program &program, const std::string &name, 6 | int binding) 7 | : m_Binding(binding) { 8 | GLint uniformBlockIndex = 9 | glGetUniformBlockIndex(program.GetId(), name.c_str()); 10 | glUniformBlockBinding(program.GetId(), uniformBlockIndex, binding); 11 | 12 | glGenBuffers(1, &m_BufferId); 13 | glBindBuffer(GL_UNIFORM_BUFFER, m_BufferId); 14 | glBufferData(GL_UNIFORM_BUFFER, static_cast(sizeof(T)), nullptr, 15 | GL_DYNAMIC_DRAW); 16 | glBindBufferBase(GL_UNIFORM_BUFFER, m_Binding, m_BufferId); 17 | 18 | glBindBuffer(GL_UNIFORM_BUFFER, 0); 19 | } 20 | 21 | template 22 | UniformBuffer::UniformBuffer(UniformBuffer &&other) { 23 | m_BufferId = other.m_BufferId; 24 | other.m_BufferId = 0; 25 | } 26 | 27 | template 28 | UniformBuffer::~UniformBuffer() { 29 | glDeleteBuffers(1, &m_BufferId); 30 | } 31 | 32 | template 33 | UniformBuffer &UniformBuffer::operator=(UniformBuffer &&other) { 34 | m_BufferId = other.m_BufferId; 35 | other.m_BufferId = 0; 36 | 37 | return *this; 38 | } 39 | 40 | template 41 | void UniformBuffer::SetData(int offset, const T &data) { 42 | glBindBuffer(GL_UNIFORM_BUFFER, m_BufferId); 43 | glBufferSubData(GL_UNIFORM_BUFFER, offset, 44 | static_cast(sizeof(T)), &data); 45 | glBindBufferBase(GL_UNIFORM_BUFFER, m_Binding, m_BufferId); 46 | } 47 | } // namespace Core 48 | -------------------------------------------------------------------------------- /include/Core/VertexArray.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CORE_VERTEX_ARRAY_HPP 2 | #define CORE_VERTEX_ARRAY_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | #include "Core/IndexBuffer.hpp" 7 | #include "Core/VertexBuffer.hpp" 8 | 9 | namespace Core { 10 | /** 11 | * @brief Wrapper for OpenGL Vertex Array Object 12 | */ 13 | class VertexArray { 14 | /** 15 | * I dislike the way `VertexBuffer` and `IndexBuffer` are handled here. This 16 | * breaks RAII principles 17 | * 18 | * TODO: Maybe find an alternative solution 19 | */ 20 | public: 21 | VertexArray(); 22 | VertexArray(const VertexArray &) = delete; 23 | VertexArray(VertexArray &&other); 24 | 25 | ~VertexArray(); 26 | 27 | VertexArray &operator=(const VertexArray &) = delete; 28 | VertexArray &operator=(VertexArray &&other); 29 | 30 | void Bind() const; 31 | void Unbind() const; 32 | 33 | void AddVertexBuffer(std::unique_ptr vertexBuffer); 34 | /** 35 | * Index buffer must be set or else there will be a segfault 36 | */ 37 | void SetIndexBuffer(std::unique_ptr indexBuffer); 38 | 39 | void DrawTriangles() const; 40 | 41 | private: 42 | GLuint m_ArrayId; 43 | 44 | std::vector> m_VertexBuffers; 45 | std::unique_ptr m_IndexBuffer; 46 | }; 47 | } // namespace Core 48 | #endif 49 | -------------------------------------------------------------------------------- /include/Core/VertexBuffer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CORE_VERTEX_BUFFER_HPP 2 | #define CORE_VERTEX_BUFFER_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | namespace Core { 7 | /** 8 | * @brief Wrapper for OpenGL Vertex Buffer Object (a.k.a `GL_ARRAY_BUFFER`) 9 | */ 10 | class VertexBuffer { 11 | public: 12 | VertexBuffer(const std::vector &vertices, 13 | unsigned int componentCount); 14 | VertexBuffer(const VertexBuffer &) = delete; 15 | VertexBuffer(VertexBuffer &&other); 16 | 17 | ~VertexBuffer(); 18 | 19 | VertexBuffer &operator=(const VertexBuffer &) = delete; 20 | VertexBuffer &operator=(VertexBuffer &&other); 21 | 22 | unsigned int GetComponentCount() const { return m_ComponentCount; } 23 | GLenum GetType() const { return m_Type; } 24 | 25 | void Bind() const; 26 | void Unbind() const; 27 | 28 | private: 29 | GLuint m_BufferId; 30 | 31 | unsigned int m_ComponentCount; 32 | GLenum m_Type = GL_FLOAT; 33 | }; 34 | } // namespace Core 35 | #endif 36 | -------------------------------------------------------------------------------- /include/Util/Animation.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_ANIMATION_HPP 2 | #define UTIL_ANIMATION_HPP 3 | 4 | #include "pch.hpp" 5 | 6 | #include 7 | 8 | #include "Core/Drawable.hpp" 9 | 10 | #include "Util/Image.hpp" 11 | 12 | namespace Util { 13 | /** 14 | * @class Animation 15 | * @brief Class representing an animation with frames. 16 | */ 17 | class Animation : public Core::Drawable { 18 | public: 19 | /** 20 | * @brief Enum representing the state of the animation. 21 | */ 22 | enum class State { 23 | PLAY, /**< Animation is playing. */ 24 | PAUSE, /**< Animation is paused. */ 25 | COOLDOWN, /**< Animation is COOLDOWN. */ 26 | ENDED /**< Animation is Ended(looping == false && Animate end.). */ 27 | }; 28 | 29 | /** 30 | * @brief Constructor for Animation class. 31 | * @param paths Vector of file paths for the frames. 32 | * @param play Whether the animation should play right away. 33 | * @param interval Interval between frames in milliseconds. 34 | * @param looping Whether the animation should loop. 35 | * @param cooldown Cooldown time in milliseconds before the animation can 36 | * restart. 37 | */ 38 | Animation(const std::vector &paths, bool play, 39 | std::size_t interval, bool looping = true, 40 | std::size_t cooldown = 100, bool useAA = true); 41 | 42 | /** 43 | * @brief Get the interval between frames. 44 | * @return Interval between frames in milliseconds. 45 | */ 46 | int GetInterval() const { return m_Interval; } 47 | 48 | /** 49 | * @brief Check if the animation loops. 50 | * @return True if the animation loops, false otherwise. 51 | */ 52 | bool GetLooping() const { return m_Looping; } 53 | 54 | /** 55 | * @brief Get the cooldown time. 56 | * @return Cooldown time in milliseconds. 57 | */ 58 | int GetCooldown() const { return m_Cooldown; } 59 | 60 | /** 61 | * @brief Get the index of the current frame. 62 | * @return Index of the current frame. 63 | */ 64 | std::size_t GetCurrentFrameIndex() const { return m_Index; } 65 | 66 | /** 67 | * @brief Get the total number of frames in the animation. 68 | * @return Total number of frames. 69 | */ 70 | std::size_t GetFrameCount() const { return m_Frames.size(); } 71 | 72 | /** 73 | * @brief Get the current state of the animation 74 | * @return The current state of the animation 75 | */ 76 | State GetState() const { return m_State; } 77 | 78 | /** 79 | * @brief Get the size of the current frame. 80 | * @return Size of the current frame. 81 | */ 82 | glm::vec2 GetSize() const override { return m_Frames[m_Index]->GetSize(); } 83 | 84 | /** 85 | * @brief Set the interval between frames. 86 | * @param interval Interval between frames in milliseconds. 87 | */ 88 | void SetInterval(int interval) { m_Interval = interval; } 89 | 90 | /** 91 | * @brief Set whether the animation loops. 92 | * @param looping True to enable looping, false to disable. 93 | */ 94 | void SetLooping(bool looping) { m_Looping = looping; } 95 | 96 | /** 97 | * @brief Set the cooldown time. 98 | * @param cooldown Cooldown time in milliseconds. 99 | */ 100 | void SetCooldown(int cooldown) { m_Cooldown = cooldown; } 101 | 102 | /** 103 | * @brief Sets whether anti-aliasing (AA) should be enabled or disabled. 104 | * 105 | * @param useAA A boolean value indicating whether anti-aliasing should be 106 | * enabled (true) or disabled (false). 107 | * 108 | * @note This function only sets the internal flag for anti-aliasing and 109 | * does not directly affect rendering. The actual effect of anti-aliasing 110 | * depends on the rendering pipeline and the graphics hardware capabilities. 111 | * 112 | * @sa https://en.wikipedia.org/wiki/Spatial_anti-aliasing 113 | */ 114 | void UseAntiAliasing(bool useAA); 115 | 116 | /** 117 | * @brief Set the current frame of the animation. 118 | * @param index Index of the frame to set as current. 119 | */ 120 | void SetCurrentFrame(std::size_t index); 121 | 122 | /** 123 | * @brief Draw the current frame. 124 | * @param transform Transformation matrix for drawing. 125 | * @param zIndex Z-index for drawing. 126 | */ 127 | void Draw(const Core::Matrices &data) override; 128 | 129 | /** 130 | * @brief Start playing the animation. 131 | * If the animation is already playing, this method won't do anything. 132 | * If the animation has ended and `looping` is set to `false`, this would 133 | * replay the animation once. 134 | */ 135 | void Play(); 136 | 137 | /** 138 | * @brief Pause the animation. 139 | * If the animation has already been paused, this method won't do anything. 140 | */ 141 | void Pause(); 142 | 143 | private: 144 | /** 145 | * @brief Update the animation frames. 146 | */ 147 | void Update(); 148 | 149 | private: 150 | std::vector> m_Frames; 151 | State m_State; 152 | double m_Interval; 153 | bool m_Looping; 154 | std::size_t m_Cooldown; 155 | bool m_IsChangeFrame = false; 156 | 157 | unsigned long m_CooldownEndTime = 0; 158 | double m_TimeBetweenFrameUpdate = 0; 159 | 160 | std::size_t m_Index = 0; 161 | }; 162 | } // namespace Util 163 | 164 | #endif 165 | -------------------------------------------------------------------------------- /include/Util/AssetStore.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_ASSET_STORE_HPP 2 | #define UTIL_ASSET_STORE_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | #include 7 | 8 | namespace Util { 9 | /** 10 | * @brief A class template for managing assets. 11 | * 12 | * The AssetStore class template provides functionality for loading, storing, 13 | * and accessing potentially expensive resources. It uses a loader function to 14 | * load assets from filepaths and stores them in an unordered map for efficient 15 | * retrieval. 16 | * 17 | * @tparam T The type of assets managed by the store. 18 | */ 19 | template 20 | class AssetStore { 21 | public: 22 | /** 23 | * @brief Constructs an AssetStore object with the specified loader 24 | * function. 25 | * 26 | * @param loader The function used to load assets of type T from filepaths. 27 | * Missing files should be handled inside loader. 28 | */ 29 | AssetStore(std::function loader) 30 | : m_Loader(loader) {} 31 | 32 | /** 33 | * @brief Preload resources for future use. 34 | * 35 | * Calling this function before using the resource is optional and is 36 | * reserved as an optimization technique. 37 | * 38 | * @param filepath The filepath of the asset to load. 39 | */ 40 | void Load(const std::string &filepath); 41 | 42 | /** 43 | * @brief Retrieves the asset associated with the specified filepath. 44 | * 45 | * If the requested resource in not already in the store, this function will 46 | * load the resource using `loader` and cache it. This should be the default 47 | * way of loading resources. 48 | * 49 | * @param filepath The filepath of the asset to retrieve. 50 | * @return A shared pointer to the asset, or nullptr if not found. 51 | */ 52 | T Get(const std::string &filepath); 53 | 54 | /** 55 | * @brief Removes the asset associated with the specified filepath from the 56 | * store. 57 | * 58 | * It is generally not required to manually manage resources in the store 59 | * unless the program hits a memory limit. This operation does nothing if 60 | * the filepath is not in the store. 61 | * 62 | * @param filepath The filepath of the asset to remove. 63 | */ 64 | void Remove(const std::string &filepath); 65 | 66 | private: 67 | std::function m_Loader; 68 | 69 | std::unordered_map m_Map; 70 | }; 71 | } // namespace Util 72 | 73 | #include "Util/AssetStore.inl" 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /include/Util/AssetStore.inl: -------------------------------------------------------------------------------- 1 | #include "Util/AssetStore.hpp" 2 | 3 | namespace Util { 4 | template 5 | void AssetStore::Load(const std::string &filepath) { 6 | m_Map[filepath] = m_Loader(filepath); 7 | } 8 | 9 | template 10 | T AssetStore::Get(const std::string &filepath) { 11 | auto result = m_Map.find(filepath); 12 | if (result != m_Map.end()) { 13 | return result->second; 14 | } 15 | 16 | Load(filepath); 17 | 18 | return m_Map[filepath]; 19 | } 20 | 21 | template 22 | void AssetStore::Remove(const std::string &filepath) { 23 | m_Map.erase(filepath); 24 | } 25 | } // namespace Util 26 | -------------------------------------------------------------------------------- /include/Util/BGM.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_BGM_HPP 2 | #define UTIL_BGM_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | #include "Util/AssetStore.hpp" 7 | 8 | namespace Util { 9 | /** 10 | * @class BGM 11 | * @brief Class for handling background music. 12 | * @note There can be only one BGM object playing at a time. 13 | * If a BGM object is playing and another BGM object is played, 14 | * the previous one will stop playing. 15 | * @see Util::SFX 16 | */ 17 | class BGM { 18 | public: 19 | BGM() = delete; 20 | 21 | /** 22 | * @brief Constructor that initializes the BGM object and loads the music 23 | * from the specified file path. 24 | * @param path The file path of the background music to be loaded. 25 | */ 26 | explicit BGM(const std::string &path); 27 | 28 | /** 29 | * @brief Deleted copy constructor to prevent copying of BGM objects. 30 | */ 31 | BGM(const BGM &) = delete; 32 | 33 | /** 34 | * @brief Deleted copy assignment operator to prevent copying of BGM 35 | * objects. 36 | */ 37 | BGM &operator=(const BGM &) = delete; 38 | 39 | /** 40 | * @brief Retrieves the current volume of the background music. 41 | * @return The current volume of the background music. 42 | */ 43 | int GetVolume() const; 44 | 45 | /** 46 | * @brief Sets the volume of the background music. 47 | * @param volume The desired volume level for the background music. The 48 | * valid range is [0, 128].
49 | * A value of 0 mutes the music, and a value of 128 50 | * sets the maximum volume. 51 | */ 52 | void SetVolume(int volume); 53 | 54 | /** 55 | * @brief Loads the background music from the specified file path. 56 | * @param path The file path of the background music to be loaded. 57 | */ 58 | void LoadMedia(const std::string &path); 59 | 60 | /** 61 | * @brief Increases the volume of the background music by one. 62 | * @param step The amount to increase the volume by. 63 | */ 64 | void VolumeUp(int step = 1); 65 | 66 | /** 67 | * @brief Decreases the volume of the background music by one. 68 | * @param step The amount to decrease the volume by. 69 | */ 70 | void VolumeDown(int step = 1); 71 | 72 | /** 73 | * @brief Plays the background music. 74 | * @param loop The number of times the music will loop.
75 | * A value of -1 means it will loop indefinitely.
76 | * A non-negative value means it will loop that many times.
77 | * Default value: -1 78 | * @note Calling this function stops any currently playing music and plays. 79 | */ 80 | void Play(int loop = -1); 81 | 82 | /** 83 | * @brief Fades in the background music gradually. 84 | * @param tick The duration of the fade-in effect, in milliseconds. 85 | * @param loop The number of times the music will loop after the fade-in is 86 | * complete.
A value of -1 means it will loop 87 | * indefinitely. 88 | */ 89 | void FadeIn(int tick, int loop = -1); 90 | 91 | /** 92 | * @brief Fades out the background music gradually. 93 | * @param tick The duration of the fade-out effect, in milliseconds. 94 | */ 95 | void FadeOut(int tick); 96 | 97 | /** 98 | * @brief Pauses the currently playing background music. 99 | * @note This function has no effect if there is no background music 100 | * currently playing. 101 | */ 102 | void Pause(); 103 | 104 | /** 105 | * @brief Resumes the paused background music. 106 | * @note This function has no effect if there is no paused background music. 107 | */ 108 | void Resume(); 109 | 110 | private: 111 | static Util::AssetStore> s_Store; 112 | 113 | private: 114 | std::shared_ptr m_BGM; 115 | }; 116 | 117 | } // namespace Util 118 | 119 | #endif // UTIL_BGM_HPP 120 | -------------------------------------------------------------------------------- /include/Util/Base64.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_BASE64_HPP 2 | #define UTIL_BASE64_HPP 3 | 4 | /* 5 | * The code to decode the base64 in compilation time is powered by 6 | * https://stackoverflow.com/a/59604914 It's used to decode the base64 string in 7 | * compilation time and improve the efficiency. Actually it's dirty, but we just 8 | * treat as a utility and won't maintain it, so just make sure it can work. 9 | */ 10 | #include 11 | 12 | static constexpr size_t DecodeBase64Length(const char *s) { 13 | size_t len = std::char_traits::length(s); 14 | if (s[len - 2] == '=') 15 | return (len / 4) * 3 - 2; 16 | else if (s[len - 1] == '=') 17 | return (len / 4) * 3 - 1; 18 | else 19 | return (len / 4) * 3; 20 | } 21 | 22 | static constexpr std::array PrepareBase64DecodeTable() { 23 | std::array T{0}; 24 | // breaks constexpr: T.fill(-1) or missing initialization 25 | for (int i = 0; i < 256; i++) 26 | T[i] = -1; 27 | for (int i = 0; i < 64; i++) 28 | T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 29 | [i]] = i; 30 | return T; 31 | } 32 | 33 | template 34 | static constexpr std::array 35 | DecodeBase64(std::string_view b64Str) { 36 | constexpr auto T = PrepareBase64DecodeTable(); 37 | std::array out = {std::byte(0)}; 38 | int valb = -8; 39 | for (size_t i = 0, val = 0, posOut = 0; 40 | i < b64Str.length() && T[b64Str[i]] != -1; i++) { 41 | val = (val << 6) + T[b64Str[i]]; 42 | valb += 6; 43 | if (valb >= 0) { 44 | out[posOut] = std::byte((val >> valb) & 0xFF); 45 | posOut += 1; 46 | valb -= 8; 47 | } 48 | } 49 | return out; 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /include/Util/Color.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_COLOR_HPP 2 | #define UTIL_COLOR_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | #include 6 | namespace Util { 7 | /** 8 | * @enum Colors 9 | * @brief A enum class that includes common colors. 10 | */ 11 | enum class Colors : Uint32; 12 | /** 13 | * @class Color 14 | * @brief A class representing a color. 15 | * 16 | * This class is an extend of glm::vec4. It provides functionalities to 17 | * convert from and to SDL_Color format. 18 | */ 19 | class Color : public glm::vec4 { 20 | public: 21 | using glm::vec4::vec; 22 | 23 | /* 24 | * @brief Parameterized constructor. 25 | * 26 | * @param vec The color in glm::vec4 format. 27 | */ 28 | Color(const glm::vec4 &vec) 29 | : glm::vec4(vec) {} 30 | 31 | /* 32 | * @brief Parameterized constructor. 33 | * 34 | * @param r The red component of the color. 35 | * @param g The green component of the color. 36 | * @param b The blue component of the color. 37 | * @param a The alpha component of the color. 38 | */ 39 | Color(Uint8 r, Uint8 g, Uint8 b, Uint8 a = 255) 40 | : glm::vec4(r, g, b, a) {} 41 | 42 | /* 43 | * @brief Parameterized constructor. 44 | */ 45 | Color(glm::vec4 &&vec) 46 | : glm::vec4(std::move(vec)) {} 47 | 48 | /* 49 | * @brief Parameterized constructor. 50 | * 51 | * @param color The color in SDL_Color format. 52 | */ 53 | Color(const SDL_Color &color) 54 | : glm::vec4({ 55 | color.r, 56 | color.g, 57 | color.b, 58 | color.a, 59 | }) {} 60 | 61 | /** 62 | * @brief Converts the color to SDL_Color format. 63 | * 64 | * @return The color in SDL_Color format. 65 | */ 66 | SDL_Color ToSdlColor() const { 67 | return SDL_Color{ 68 | static_cast(r), 69 | static_cast(g), 70 | static_cast(b), 71 | static_cast(a), 72 | }; 73 | } 74 | /** 75 | * @brief Converts the color to a string representation. 76 | * 77 | * This method returns a string representation of the color in the format 78 | * "Color(r,g,b,a)". The values r, g, b, and a represent the red, green, 79 | * blue, and alpha components of the color, respectively. 80 | * 81 | * @return A string representation of the color. 82 | */ 83 | std::string ToString() const { 84 | return fmt::v8::format("Color({},{},{},{})", r, g, b, a); 85 | } 86 | 87 | /** 88 | * @brief Get Color From RGB Values 89 | * @param r Red Value (0-255) 90 | * @param g Green Value (0-255) 91 | * @param b Blue Value (0-255) 92 | * @param a Alpha Value (0-255), default is 255 93 | * @return Color 94 | */ 95 | static Color FromRGB(Uint8 r, Uint8 g, Uint8 b, Uint8 a = 255); 96 | 97 | /** 98 | * @brief Get Color From HSL Values 99 | * @param h Hue Value (0-1) 100 | * @param s Saturation Value (0-1) 101 | * @param l Lightness Value (0-1) 102 | * @param a Alpha Value (0-1), default is 1.0 103 | * @return Color 104 | */ 105 | static Color FromHSL(float h, float s, float l, float a = 1.0F); 106 | 107 | /** 108 | * @brief Get Color From HSV Values 109 | * @param h Hue Value (0-1) 110 | * @param s Saturation Value (0-1) 111 | * @param v Value (0-1) 112 | * @param a Alpha Value (0-1), default is 1.0 113 | * @return Color 114 | */ 115 | static Color FromHSV(float h, float s, float v, float a = 1.0F); 116 | 117 | /** 118 | * @brief Get Color From Hex RGB Value, alpha value is necessary 119 | * @param hex Hex Value (0x00000000 - 0xFFFFFFFF) 120 | * @return Color 121 | */ 122 | static Color FromHex(Uint32 hex); 123 | 124 | /** 125 | * @brief Get Color From Hex RGB String, alpha value is necessary 126 | * @param hex Hex String ("00000000" - "FFFFFFFF") 127 | * @return Color 128 | */ 129 | static Color FromHex(const std::string &hex); 130 | 131 | /** 132 | * @brief Get Color From name within Util::Colors 133 | * @param name Color Name in Util::Colors 134 | * @return Color 135 | * @see Util::Colors 136 | */ 137 | static Color FromName(const Util::Colors &name); 138 | }; 139 | 140 | enum class Colors : Uint32 { 141 | ALICE_BLUE = 0xF0F8FF, 142 | ANTIQUE_WHITE = 0xFAEBD7, 143 | AQUA = 0x00FFFF, 144 | AQUAMARINE = 0x7FFFD4, 145 | AZURE = 0xF0FFFF, 146 | BEIGE = 0xF5F5DC, 147 | BISQUE = 0xFFE4C4, 148 | BLACK = 0x000000, 149 | BLANCHED_ALMOND = 0xFFEBCD, 150 | BLUE = 0x0000FF, 151 | BLUE_VIOLET = 0x8A2BE2, 152 | BROWN = 0xA52A2A, 153 | BURLY_WOOD = 0xDEB887, 154 | CADET_BLUE = 0x5F9EA0, 155 | CHARTREUSE = 0x7FFF00, 156 | CHOCOLATE = 0xD2691E, 157 | CORAL = 0xFF7F50, 158 | CORNFLOWER_BLUE = 0x6495ED, 159 | CORNSILK = 0xFFF8DC, 160 | CRIMSON = 0xDC143C, 161 | CYAN = 0x00FFFF, 162 | DARK_BLUE = 0x00008B, 163 | DARK_CYAN = 0x008B8B, 164 | DARK_GOLDENROD = 0xB8860B, 165 | DARK_GRAY = 0xA9A9A9, 166 | DARK_GREEN = 0x006400, 167 | DARK_KHAKI = 0xBDB76B, 168 | DARK_MAGENTA = 0x8B008B, 169 | DARK_OLIVE_GREEN = 0x556B2F, 170 | DARK_ORANGE = 0xFF8C00, 171 | DARK_ORCHID = 0x9932CC, 172 | DARK_RED = 0x8B0000, 173 | DARK_SALMON = 0xE9967A, 174 | DARK_SEA_GREEN = 0x8FBC8F, 175 | DARK_SLATE_BLUE = 0x483D8B, 176 | DARK_SLATE_GRAY = 0x2F4F4F, 177 | DARK_TURQUOISE = 0x00CED1, 178 | DARK_VIOLET = 0x9400D3, 179 | DEEP_PINK = 0xFF1493, 180 | DEEP_SKY_BLUE = 0x00BFFF, 181 | DIM_GRAY = 0x696969, 182 | DODGER_BLUE = 0x1E90FF, 183 | FIREBRICK = 0xB22222, 184 | FLORAL_WHITE = 0xFFFAF0, 185 | FOREST_GREEN = 0x228B22, 186 | FUCHSIA = 0xFF00FF, 187 | GAINSBORO = 0xDCDCDC, 188 | GHOST_WHITE = 0xF8F8FF, 189 | GOLD = 0xFFD700, 190 | GOLDENROD = 0xDAA520, 191 | GRAY = 0x808080, 192 | GREEN = 0x008000, 193 | GREEN_YELLOW = 0xADFF2F, 194 | HONEYDEW = 0xF0FFF0, 195 | HOT_PINK = 0xFF69B4, 196 | INDIAN_RED = 0xCD5C5C, 197 | INDIGO = 0x4B0082, 198 | IVORY = 0xFFFFF0, 199 | KHAKI = 0xF0E68C, 200 | LAVENDER = 0xE6E6FA, 201 | LAVENDER_BLUSH = 0xFFF0F5, 202 | LAWN_GREEN = 0x7CFC00, 203 | LEMON_CHIFFON = 0xFFFACD, 204 | LIGHT_BLUE = 0xADD8E6, 205 | LIGHT_CORAL = 0xF08080, 206 | LIGHT_CYAN = 0xE0FFFF, 207 | LIGHT_GOLDENROD_YELLOW = 0xFAFAD2, 208 | LIGHT_GRAY = 0xD3D3D3, 209 | LIGHT_GREEN = 0x90EE90, 210 | LIGHT_PINK = 0xFFB6C1, 211 | LIGHT_SALMON = 0xFFA07A, 212 | LIGHT_SEA_GREEN = 0x20B2AA, 213 | LIGHT_SKY_BLUE = 0x87CEFA, 214 | LIGHT_SLATE_GRAY = 0x778899, 215 | LIGHT_STEEL_BLUE = 0xB0C4DE, 216 | LIGHT_YELLOW = 0xFFFFE0, 217 | LIME = 0x00FF00, 218 | LIME_GREEN = 0x32CD32, 219 | LINEN = 0xFAF0E6, 220 | MAGENTA = 0xFF00FF, 221 | MAROON = 0x800000, 222 | MEDIUM_AQUAMARINE = 0x66CDAA, 223 | MEDIUM_BLUE = 0x0000CD, 224 | MEDIUM_ORCHID = 0xBA55D3, 225 | MEDIUM_PURPLE = 0x9370DB, 226 | MEDIUM_SEA_GREEN = 0x3CB371, 227 | MEDIUM_SLATE_BLUE = 0x7B68EE, 228 | MEDIUM_SPRING_GREEN = 0x00FA9A, 229 | MEDIUM_TURQUOISE = 0x48D1CC, 230 | MEDIUM_VIOLET_RED = 0xC71585, 231 | MIDNIGHT_BLUE = 0x191970, 232 | MINT_CREAM = 0xF5FFFA, 233 | MISTY_ROSE = 0xFFE4E1, 234 | MOCCASIN = 0xFFE4B5, 235 | NAVAJO_WHITE = 0xFFDEAD, 236 | NAVY = 0x000080, 237 | OLD_LACE = 0xFDF5E6, 238 | OLIVE = 0x808000, 239 | OLIVE_DRAB = 0x6B8E23, 240 | ORANGE = 0xFFA500, 241 | ORANGE_RED = 0xFF4500, 242 | ORCHID = 0xDA70D6, 243 | PALE_GOLDENROD = 0xEEE8AA, 244 | PALE_GREEN = 0x98FB98, 245 | PALE_TURQUOISE = 0xAFEEEE, 246 | PALE_VIOLET_RED = 0xDB7093, 247 | PAPAYA_WHIP = 0xFFEFD5, 248 | PEACH_PUFF = 0xFFDAB9, 249 | PERU = 0xCD853F, 250 | PINK = 0xFFC0CB, 251 | PLUM = 0xDDA0DD, 252 | POWDER_BLUE = 0xB0E0E6, 253 | PURPLE = 0x800080, 254 | REBECCA_PURPLE = 0x663399, 255 | RED = 0xFF0000, 256 | ROSY_BROWN = 0xBC8F8F, 257 | ROYAL_BLUE = 0x4169E1, 258 | SADDLE_BROWN = 0x8B4513, 259 | SALMON = 0xFA8072, 260 | SANDY_BROWN = 0xF4A460, 261 | SEA_GREEN = 0x2E8B57, 262 | SEASHELL = 0xFFF5EE, 263 | SIENNA = 0xA0522D, 264 | SILVER = 0xC0C0C0, 265 | SKY_BLUE = 0x87CEEB, 266 | SLATE_BLUE = 0x6A5ACD, 267 | SLATE_GRAY = 0x708090, 268 | SNOW = 0xFFFAFA, 269 | SPRING_GREEN = 0x00FF7F, 270 | STEEL_BLUE = 0x4682B4, 271 | TAN = 0xD2B48C, 272 | TEAL = 0x008080, 273 | THISTLE = 0xD8BFD8, 274 | TOMATO = 0xFF6347, 275 | TURQUOISE = 0x40E0D0, 276 | VIOLET = 0xEE82EE, 277 | WHEAT = 0xF5DEB3, 278 | WHITE = 0xFFFFFF, 279 | WHITE_SMOKE = 0xF5F5F5, 280 | YELLOW = 0xFFFF00, 281 | YELLOW_GREEN = 0x9ACD32 282 | }; 283 | } // namespace Util 284 | 285 | #endif 286 | -------------------------------------------------------------------------------- /include/Util/Docs.hpp: -------------------------------------------------------------------------------- 1 | #error This file is only for documentation purposes only 2 | 3 | /** 4 | * @brief Useful tools for development 5 | */ 6 | namespace Util {} 7 | -------------------------------------------------------------------------------- /include/Util/GameObject.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_GAME_OBJECT_HPP 2 | #define UTIL_GAME_OBJECT_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | #include "Core/Drawable.hpp" 7 | 8 | #include "Util/Transform.hpp" 9 | 10 | namespace Util { 11 | /** 12 | * @class GameObject 13 | * @brief A class representing a game object. 14 | * 15 | * This class encapsulates the properties and behaviors of a game object. 16 | * @note This is an abstract class. Inherit from this class to create your own 17 | * game objects. 18 | * 19 | */ 20 | class GameObject { 21 | public: 22 | Util::Transform m_Transform; // IDC if this should be here. 23 | 24 | public: 25 | /** 26 | * @brief Default constructor. 27 | */ 28 | GameObject() = default; 29 | 30 | /** 31 | * @brief Parameterized constructor. 32 | * 33 | * @param drawable The Util::Drawable component of the game object. 34 | * @param zIndex The z-index of the game object. 35 | * @param visible The visibility of the game object. 36 | * @param children The children of the game object. 37 | */ 38 | GameObject(const std::shared_ptr &drawable, 39 | const float zIndex, const glm::vec2 &pivot = {0, 0}, 40 | const bool visible = true, 41 | const std::vector> &children = 42 | std::vector>()) 43 | : m_Drawable(drawable), 44 | m_Children(children), 45 | m_ZIndex(zIndex), 46 | m_Visible(visible), 47 | m_Pivot(pivot) {} 48 | 49 | /** 50 | * @brief Copy constructor. 51 | * @param other 52 | * 53 | * @note This is a shallow copy constructor, meaning the m_Drawable points 54 | * to the same reference as same as `other`'s does. 55 | */ 56 | GameObject(const GameObject &other) = default; 57 | 58 | /** 59 | * @brief Default move constructor.. 60 | */ 61 | GameObject(GameObject &&other) = default; 62 | 63 | /** 64 | * @brief Default destructor. 65 | */ 66 | virtual ~GameObject() = default; 67 | 68 | // Deleted assignment operator. 69 | GameObject &operator=(const GameObject &other) = delete; 70 | 71 | /** 72 | * @brief Get the z-index of the game object. 73 | * 74 | * @return The z-index of the game object. 75 | */ 76 | float GetZIndex() const { return m_ZIndex; } 77 | 78 | /** 79 | * @brief Get the transform of the game object. 80 | * 81 | * @return The transform of the game object. 82 | */ 83 | Transform GetTransform() const { return m_Transform; } 84 | 85 | /** 86 | * @brief Get the size of its drawable component. 87 | * 88 | * @return vec2(x, y) representing the size of the drawable component. 89 | */ 90 | glm::vec2 GetScaledSize() const { 91 | return m_Drawable->GetSize() * m_Transform.scale; 92 | }; 93 | 94 | /** 95 | * @brief Get the children of the game object. 96 | * 97 | * @return The children of the game object. 98 | */ 99 | const std::vector> &GetChildren() const { 100 | return m_Children; 101 | } 102 | 103 | /** 104 | * @brief Set the pivot of the game object. 105 | * 106 | * @param pivot The pivot of the game object. 107 | */ 108 | void SetPivot(const glm::vec2 &pivot) { m_Pivot = pivot; } 109 | 110 | /** 111 | * @brief Set the z-index of the game object. 112 | * z-index is used to determine the order in which game objects are drawn, 113 | * the greater z-index, the topper the its Util::Drawable is 114 | * 115 | * @param index The new z-index of the game object. 116 | */ 117 | void SetZIndex(float index) { m_ZIndex = index; } 118 | 119 | /** 120 | * @brief Set the drawable component of the game object. 121 | * 122 | * @param drawable The new drawable component of the game object. 123 | */ 124 | void SetDrawable(const std::shared_ptr &drawable) { 125 | m_Drawable = drawable; 126 | } 127 | 128 | /** 129 | * @brief Set the visibility of the game object. 130 | * 131 | * @param visible The new visibility of the game object. 132 | */ 133 | void SetVisible(const bool visible) { m_Visible = visible; } 134 | 135 | /** 136 | * @brief Add a child to the game object. 137 | * 138 | * @param child The new child of the game object. 139 | */ 140 | void AddChild(const std::shared_ptr &child) { 141 | m_Children.push_back(child); 142 | } 143 | 144 | /** 145 | * @brief Remove a child from the game object. 146 | * 147 | * @param child The child to be removed. 148 | */ 149 | void RemoveChild(const std::shared_ptr &child) { 150 | m_Children.erase( 151 | std::remove(m_Children.begin(), m_Children.end(), child), 152 | m_Children.end()); 153 | } 154 | 155 | void Draw(); 156 | 157 | protected: 158 | std::shared_ptr m_Drawable = nullptr; 159 | std::vector> m_Children; 160 | 161 | float m_ZIndex = 0; 162 | bool m_Visible = true; 163 | glm::vec2 m_Pivot = {0, 0}; 164 | }; 165 | } // namespace Util 166 | #endif 167 | -------------------------------------------------------------------------------- /include/Util/Image.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_IMAGE_HPP 2 | #define UTIL_IMAGE_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | #include 7 | 8 | #include "Core/Drawable.hpp" 9 | #include "Core/Program.hpp" 10 | #include "Core/Texture.hpp" 11 | #include "Core/UniformBuffer.hpp" 12 | #include "Core/VertexArray.hpp" 13 | 14 | #include "Util/AssetStore.hpp" 15 | 16 | namespace Util { 17 | /** 18 | * @class Image 19 | * @brief A class representing an image. 20 | * 21 | * This class encapsulates the properties and behaviors of an image. 22 | * It includes properties such as texture and surface. 23 | * It also includes behaviors such as drawing the image. 24 | */ 25 | class Image : public Core::Drawable { 26 | public: 27 | /** 28 | * @brief Constructor that takes a file path to the image. 29 | * 30 | * @param filepath The file path to the image. 31 | * @param useAA Flag indicating whether anti-aliasing should be enabled 32 | * (default is true). 33 | */ 34 | Image(const std::string &filepath, bool useAA = true); 35 | 36 | /** 37 | * @brief Retrieves the size of the image. 38 | * 39 | * This function returns the size of the image. 40 | * 41 | * @return The size of the image as a vec2(x, y). 42 | */ 43 | glm::vec2 GetSize() const override { return m_Size; }; 44 | 45 | /** 46 | * @brief Sets the image to the specified file path. 47 | * 48 | * This function sets the image to the specified file path. 49 | * 50 | * @param filepath The file path to the image. 51 | */ 52 | void SetImage(const std::string &filepath); 53 | 54 | /** 55 | * @brief Sets whether anti-aliasing (AA) should be enabled or disabled. 56 | * 57 | * @param useAA A boolean value indicating whether anti-aliasing should be 58 | * enabled (true) or disabled (false). 59 | * 60 | * @note This function only sets the internal flag for anti-aliasing and 61 | * does not directly affect rendering. The actual effect of anti-aliasing 62 | * depends on the rendering pipeline and the graphics hardware capabilities. 63 | * 64 | * @sa https://en.wikipedia.org/wiki/Spatial_anti-aliasing 65 | */ 66 | void UseAntiAliasing(bool useAA); 67 | 68 | /** 69 | * @brief Draws the image with a given transform and z-index. 70 | * 71 | * This function draws the image at the specified z-index and applies the 72 | * given transform. 73 | * 74 | * @param transform The transform to apply to the image. 75 | * @param zIndex The z-index at which to draw the image. 76 | */ 77 | void Draw(const Core::Matrices &data) override; 78 | 79 | private: 80 | void InitProgram(); 81 | void InitVertexArray(); 82 | void InitUniformBuffer(); 83 | 84 | static constexpr int UNIFORM_SURFACE_LOCATION = 0; 85 | 86 | static std::unique_ptr s_Program; 87 | static std::unique_ptr s_VertexArray; 88 | std::unique_ptr> m_UniformBuffer; 89 | 90 | static Util::AssetStore> s_Store; 91 | 92 | private: 93 | std::unique_ptr m_Texture = nullptr; 94 | 95 | std::string m_Path; 96 | glm::vec2 m_Size; 97 | }; 98 | } // namespace Util 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /include/Util/Input.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_EVENT_HPP 2 | #define UTIL_EVENT_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | #include // for SDL_Event 7 | #include // for Uint8 8 | 9 | #include "Util/Keycode.hpp" // for Keycode 10 | 11 | #include "Util/Position.hpp" // Util::PTSDPosition 12 | 13 | namespace Util { 14 | 15 | /** 16 | * @class Input 17 | * @brief The Input class provides access to keyboard and mouse input. 18 | * @note This class is a singleton and constructable. Use is as follows: \n 19 | `Util::Input::IsKeyPressed(Keycode::A)`, 20 | `Util::Input::IsLButtonDown()`, etc. 21 | */ 22 | class Input { 23 | public: 24 | Input() = delete; 25 | Input(const Input &) = delete; 26 | Input(Input &&) = delete; 27 | ~Input() = delete; 28 | Input &operator=(const Input &) = delete; 29 | 30 | /** 31 | * @brief Retrieves the scroll distance of an element.\n 32 | * 33 | * @details The scroll distance is the distance that the mouse wheel has 34 | * been scrolled. The distance is expressed in multiples or fractions of 35 | * lines; for example, if the mouse wheel is rotated three lines downward, 36 | * the scroll distance is {-1.0F, 0.0F}. If the mouse wheel is rotated three 37 | * lines upward, the scroll distance is {1.0F, 0.0F}. If the mouse wheel is 38 | * rotated three lines right, the scroll distance is {0.0F, 1.0F}. If the 39 | * mouse wheel is rotated three lines left, the scroll distance is {0.0F, 40 | * -1.0F}. 41 | * 42 | * @return The scroll distance as vec2(x,y). 43 | */ 44 | static glm::vec2 GetScrollDistance(); 45 | 46 | /** 47 | * @brief Retrieves the current position of the cursor. 48 | * 49 | * @return The cursor position as a PTSDPosition (x, y). 50 | * 51 | * @see Util::Input::SetCursorPosition() 52 | * @see Util::PTSDPosition 53 | */ 54 | static Util::PTSDPosition GetCursorPosition(); 55 | 56 | /** 57 | * @brief Check if a specific key is currently pressed. 58 | * 59 | * This function checks whether the given key is currently pressed. 60 | * 61 | * @param key The keycode of the key to check. 62 | * 63 | * @return true if `key` is currently pressed, false otherwise. 64 | * 65 | * @see Util::Keycode 66 | */ 67 | static bool IsKeyPressed(const Keycode &key); 68 | 69 | /** 70 | * @brief Check if a specific key is being pressed. 71 | * 72 | * This function checks whether the given key is currently being pressed. 73 | * 74 | * @param key The keycode of the key to check. 75 | * 76 | * @return true if `key` is currently pressed, false otherwise. 77 | * 78 | * @see Util::Keycode 79 | */ 80 | static bool IsKeyDown(const Keycode &key); 81 | 82 | /** 83 | * @brief Check if a specific key is being un-pressed. 84 | * 85 | * This function checks whether the given key is currently being un-pressed. 86 | * 87 | * @param key The keycode of the key to check. 88 | * 89 | * @return true if `key` is currently pressed, false otherwise. 90 | * 91 | * @see Util::Keycode 92 | */ 93 | static bool IsKeyUp(const Keycode &key); 94 | 95 | /** 96 | * @brief Checks if the mouse wheel is currently being scrolled. 97 | * @return A bool value representing the current state of the mouse 98 | * wheel. 99 | */ 100 | static bool IfScroll(); 101 | 102 | /** 103 | * @brief Checks if the mouse is currently moving. 104 | * @return true if the mouse is currently moving, false otherwise. 105 | */ 106 | static bool IsMouseMoving(); 107 | 108 | /** 109 | * @brief Checks if the window is closed 110 | * @return true if the window is closed, false otherwise. 111 | */ 112 | static bool IfExit(); 113 | 114 | /** 115 | * @brief Sets the position of the cursor. 116 | * @param pos The position to set the cursor to. 117 | * @note This also triggers a mouse motion event, making 118 | * Util::Input::IsMouseMoving() return true in the current update cycle. 119 | * 120 | * @see Util::Input::GetCursorPosition() 121 | * @see Util::PTSDPosition 122 | */ 123 | static void SetCursorPosition(const Util::PTSDPosition &pos); 124 | 125 | /** 126 | * @brief Updates the state of the input. 127 | * @warning DO NOT CALL THIS METHOD. It is called by context::Update() 128 | * already. 129 | */ 130 | static void Update(); 131 | 132 | private: 133 | static void UpdateKeyState(const SDL_Event *event); 134 | 135 | static SDL_Event s_Event; 136 | 137 | static Util::PTSDPosition s_CursorPosition; 138 | static glm::vec2 s_ScrollDistance; 139 | 140 | static std::unordered_map> s_KeyState; 141 | 142 | static bool s_Scroll; 143 | static bool s_MouseMoving; 144 | static bool s_Exit; 145 | 146 | static ImGuiIO s_Io; 147 | }; 148 | 149 | } // namespace Util 150 | 151 | #endif // UTIL_EVENT_HPP 152 | -------------------------------------------------------------------------------- /include/Util/LoadTextFile.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_LOAD_TEXT_FILE_HPP 2 | #define UTIL_LOAD_TEXT_FILE_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | namespace Util { 7 | /** 8 | * @brief Generic helper function that loads a text file into a `std::string`. 9 | */ 10 | std::string LoadTextFile(const std::string &filepath); 11 | } // namespace Util 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /include/Util/Logger.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_LOGGER_HPP 2 | #define UTIL_LOGGER_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | #include 7 | 8 | #include "Util/Transform.hpp" 9 | 10 | namespace Util::Logger { 11 | /** 12 | * @enum Level 13 | * @brief Enum representing the logging levels. 14 | * 15 | * This enum encapsulates the different levels of logging. 16 | */ 17 | enum class Level { 18 | TRACE, 19 | DEBUG, 20 | INFO, 21 | WARN, 22 | ERROR, 23 | CRITICAL, 24 | }; 25 | 26 | /** 27 | * @brief Initializes the logger. 28 | * 29 | * This function initializes the logger for the application. 30 | */ 31 | void Init(); 32 | 33 | /** 34 | * @brief Sets the logging level. 35 | * 36 | * This function sets the logging level for the application. 37 | * Logging levels are TRACE, DEBUG, INFO, WARN, ERROR, and CRITICAL. 38 | * @note If the current log's level is lower than the set level, the log will 39 | * be omitted. 40 | * 41 | * @param level The new logging level. 42 | */ 43 | void SetLevel(Level level); 44 | 45 | /** 46 | * @brief Gets the current logging level. 47 | * 48 | * This function returns the current logging level of the application. 49 | * 50 | * @return The current logging level. 51 | */ 52 | Level GetLevel(); 53 | 54 | #define LOG_TRACE(...) spdlog::trace(__VA_ARGS__) 55 | #define LOG_DEBUG(...) spdlog::debug(__VA_ARGS__) 56 | #define LOG_INFO(...) spdlog::info(__VA_ARGS__) 57 | #define LOG_WARN(...) spdlog::warn(__VA_ARGS__) 58 | #define LOG_ERROR(...) spdlog::error(__VA_ARGS__) 59 | #define LOG_CRITICAL(...) spdlog::critical(__VA_ARGS__) 60 | } // namespace Util::Logger 61 | 62 | /* 63 | * I have no idea what this does 64 | * 65 | * Credit to: https://github.com/fmtlib/fmt/issues/3306#issuecomment-1432711988 66 | */ 67 | // NOLINTBEGIN 68 | template 69 | struct fmt::formatter> : fmt::formatter
 {
70 |     auto format(const glm::vec &vec, format_context &ctx) const
71 |         -> decltype(ctx.out()) {
72 |         return format_to(ctx.out(), "{}", glm::to_string(vec));
73 |     }
74 | };
75 | 
76 | template 
77 | struct fmt::formatter> : fmt::formatter
 {
78 |     auto format(const glm::mat &mat, format_context &ctx) const
79 |         -> decltype(ctx.out()) {
80 |         return format_to(ctx.out(), "{}", glm::to_string(mat));
81 |     }
82 | };
83 | 
84 | template <>
85 | struct fmt::formatter : fmt::formatter {
86 |     auto format(const Util::Transform &t, format_context &ctx) const
87 |         -> decltype(ctx.out()) {
88 |         return format_to(ctx.out(), "T: {} R: {} rad S: {}", t.translation,
89 |                          t.rotation, t.scale);
90 |     }
91 | };
92 | // NOLINTEND
93 | 
94 | #endif
95 | 


--------------------------------------------------------------------------------
/include/Util/MissingTexture.hpp:
--------------------------------------------------------------------------------
 1 | #ifndef UTIL_MISSING_TEXTURE_HPP
 2 | #define UTIL_MISSING_TEXTURE_HPP
 3 | 
 4 | #include 
 5 | #include 
 6 | #include 
 7 | 
 8 | #include "Base64.hpp"
 9 | #include "Core/MissingFontTextureBase64.hpp"
10 | #include "Core/MissingImageTextureBase64.hpp"
11 | 
12 | static constexpr auto MISSING_FONT_TEXTURE_BASE64_DECODE_LENGTH =
13 |     DecodeBase64Length(MISSING_FONT_TEXTURE);
14 | static constexpr auto MISSING_FONT_TEXTURE_BASE64_DECODE =
15 |     DecodeBase64(
16 |         MISSING_FONT_TEXTURE);
17 | 
18 | static constexpr auto MISSING_IMAGE_TEXTURE_BASE64_DECODE_LENGTH =
19 |     DecodeBase64Length(MISSING_IMAGE_TEXTURE);
20 | static constexpr auto MISSING_IMAGE_TEXTURE_BASE64_DECODE =
21 |     DecodeBase64(
22 |         MISSING_IMAGE_TEXTURE);
23 | 
24 | namespace Util {
25 | SDL_Surface *GetMissingFontTextureSDLSurface();
26 | SDL_Surface *GetMissingImageTextureSDLSurface();
27 | } // namespace Util
28 | 
29 | #endif
30 | 


--------------------------------------------------------------------------------
/include/Util/Position.hpp:
--------------------------------------------------------------------------------
 1 | #ifndef PTSD_UTIL_POSITION_HPP
 2 | #define PTSD_UTIL_POSITION_HPP
 3 | 
 4 | #include 
 5 | 
 6 | namespace Util {
 7 | 
 8 | struct PTSDPosition;
 9 | 
10 | /**
11 |  * @struct SDLPosition
12 |  * @brief A class representing a position in screen coordinates.
13 |  *
14 |  * This class is used to store the X and Y coordinates of a point in a screen
15 |  * coordinate system where:
16 |  * - The origin (0, 0) is at the upper-left corner of the window.
17 |  * - X increases to the right.
18 |  * - Y increases downwards.
19 |  */
20 | struct SDLPosition {
21 | public:
22 |     const int x;
23 |     const int y;
24 |     SDLPosition() = delete;
25 | 
26 | private:
27 |     friend PTSDPosition;
28 |     SDLPosition(float x, float y)
29 |         : x(x),
30 |           y(y) {}
31 | };
32 | 
33 | /**
34 |  * @struct PTSDPosition
35 |  * @brief A class representing a position in a Cartesian coordinates.
36 |  *
37 |  * This class is used to store the X and Y coordinates of a point in a Cartesian
38 |  * coordinate system where:
39 |  * - The origin (0, 0) is at the center of the window.
40 |  * - X increases to the right.
41 |  * - Y increases upwards.
42 |  */
43 | struct PTSDPosition {
44 |     float x{};
45 |     float y{};
46 |     PTSDPosition() = delete;
47 |     static PTSDPosition FromSDL(int sdlx, int sdly);
48 |     PTSDPosition(float x, float y)
49 |         : x{x},
50 |           y{y} {};
51 |     [[deprecated("Implicit conversion will be removed. Use explicit conversion "
52 |                  "instead")]]
53 |     // `\_(:/)_/`
54 |     PTSDPosition(glm::vec2 v)
55 |         : x{v.x},
56 |           y{v.y} {};
57 | 
58 |     PTSDPosition operator+(const glm::vec2) const;
59 |     PTSDPosition operator-(const glm::vec2 vec2) const {
60 |         return (*this) + (vec2 * (-1.0f));
61 |     }
62 | 
63 |     [[deprecated("Implicit conversion will be removed. Use explicit conversion "
64 |                  "instead")]] //
65 |     operator glm::vec2() const {
66 |         return {x, y};
67 |     }
68 | 
69 |     SDLPosition ToSDLPosition() const;
70 | };
71 | 
72 | } // namespace Util
73 | 
74 | #include "Position.inl"
75 | 
76 | #endif /* PTSD_UTIL_POSITION_HPP */
77 | 


--------------------------------------------------------------------------------
/include/Util/Position.inl:
--------------------------------------------------------------------------------
 1 | #include "Util/Position.hpp"
 2 | 
 3 | #include 
 4 | 
 5 | template <>
 6 | struct fmt::formatter : fmt::formatter {
 7 |     auto format(const Util::PTSDPosition &p, format_context &ctx) const
 8 |         -> decltype(ctx.out()) {
 9 |         return format_to(ctx.out(), "PTSDPos ({}, {})", p.x, p.y);
10 |     }
11 | };
12 | 


--------------------------------------------------------------------------------
/include/Util/Renderer.hpp:
--------------------------------------------------------------------------------
 1 | #ifndef UTIL_Renderer_HPP
 2 | #define UTIL_Renderer_HPP
 3 | 
 4 | #include 
 5 | #include 
 6 | 
 7 | #include "Util/GameObject.hpp"
 8 | 
 9 | class App;
10 | 
11 | namespace Util {
12 | /**
13 |  * @class Renderer
14 |  * @brief A class handling GameObjects' Draw()
15 |  * @see Util::GameObject
16 |  */
17 | class Renderer final {
18 | public:
19 |     /**
20 |      * @brief Parameterized constructor.
21 |      *`
22 |      *
23 |      * @param children The GameObject needing to be managed by Renderer.
24 |      */
25 |     Renderer(const std::vector> &children = {});
26 | 
27 |     /**
28 |      * @brief Add a child to Renderer.
29 |      *
30 |      * @param child The GameObject needing to be managed by Renderer.
31 |      */
32 |     void AddChild(const std::shared_ptr &child);
33 | 
34 |     /**
35 |      * @brief Add children to Renderer.
36 |      *
37 |      * @param children The GameObjects needing to be managed by Renderer.
38 |      */
39 |     void AddChildren(const std::vector> &children);
40 | 
41 |     /**
42 |      * @brief Remove the child.
43 |      *
44 |      * @param child The GameObject being removed.
45 |      */
46 |     void RemoveChild(std::shared_ptr child);
47 | 
48 |     /**
49 |      * @brief Draw children according to their z-index.
50 |      *
51 |      * @note The user is not recommended to modify this function.
52 |      */
53 |     void Update();
54 | 
55 | private:
56 |     std::vector> m_Children;
57 | };
58 | } // namespace Util
59 | 
60 | #endif
61 | 


--------------------------------------------------------------------------------
/include/Util/SFX.hpp:
--------------------------------------------------------------------------------
  1 | #ifndef UTIL_SFX_HPP
  2 | #define UTIL_SFX_HPP
  3 | 
  4 | #include "pch.hpp" // IWYU pragma: export
  5 | 
  6 | #include "Util/AssetStore.hpp"
  7 | 
  8 | namespace Util {
  9 | 
 10 | /**
 11 |  * @class SFX
 12 |  * @brief Class for handling sound effects.
 13 |  * @see One should use Util::BGM for long audio files since it loads audio from
 14 |  * disk instead of memory.
 15 |  *            (https://wiki.libsdl.org/SDL2_mixer/Mix_LoadMUS#remarks)
 16 |  */
 17 | class SFX {
 18 | public:
 19 |     SFX() = delete;
 20 | 
 21 |     /**
 22 |      * @brief Constructor that initializes the SFX object and loads the sound
 23 |      *            effect from the specified file path.
 24 |      * @param path The file path of the sound effect to be loaded.
 25 |      */
 26 |     explicit SFX(const std::string &path);
 27 | 
 28 |     /**
 29 |      * @brief Deleted copy constructor to prevent copying of SFX objects.
 30 |      */
 31 |     SFX(const SFX &) = delete;
 32 | 
 33 |     /**
 34 |      * @brief Deleted copy assignment operator to prevent copying of SFX
 35 |      *            objects.
 36 |      */
 37 |     SFX &operator=(const SFX &) = delete;
 38 | 
 39 |     /**
 40 |      * @brief Retrieves the current volume of the sound effect.
 41 |      * @return The current volume of the sound effect.
 42 |      */
 43 |     int GetVolume() const;
 44 | 
 45 |     /**
 46 |      * @brief Sets the volume of the background music.
 47 |      * @param volume The desired volume level for the background music. The
 48 |      *                          valid range is [0, 128].
49 | * A value of 0 mutes the music, and a value of 128 50 | * sets the maximum volume. 51 | */ 52 | void SetVolume(int volume); 53 | 54 | /** 55 | * @brief Loads the sound effect from the specified file path. 56 | * @param path The file path of the sound effect to be loaded. 57 | */ 58 | void LoadMedia(const std::string &path); 59 | 60 | /** 61 | * @brief Increases the volume of the sound effect by one. 62 | * @param step The amount to increase the volume by. 63 | */ 64 | void VolumeUp(int step = 1); 65 | 66 | /** 67 | * @brief Decreases the volume of the sound effect by one. 68 | * @param step The amount to decrease the volume by. 69 | */ 70 | void VolumeDown(int step = 1); 71 | 72 | /** 73 | * @brief Plays the sound effect. 74 | * @param loop The number of times the sound effect will loop.
75 | * A value of 0 means it will play once (no looping). 76 | * @param duration The duration of the sound effect in milliseconds.
77 | * A value of -1 means it will play the entire 78 | * sound effect. 79 | */ 80 | void Play(int loop = 0, int duration = -1); 81 | 82 | /** 83 | * @brief Fades in the sound effect gradually. 84 | * @param tick The duration of the fade-in effect, in milliseconds. 85 | * @param loop The number of times the sound effect will loop after the 86 | * fade-in is complete.
87 | * A value of -1 means it will loop indefinitely. 88 | * @param duration The duration of the sound effect in milliseconds.
89 | * A value of -1 means it will play the entire 90 | * sound effect. 91 | */ 92 | void FadeIn(unsigned int tick, int oop = -1, unsigned int duration = -1); 93 | 94 | private: 95 | static Util::AssetStore> s_Store; 96 | 97 | private: 98 | std::shared_ptr m_Chunk; 99 | }; 100 | 101 | } // namespace Util 102 | #endif // UTIL_SFX_HPP 103 | -------------------------------------------------------------------------------- /include/Util/Text.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_TEXT_HPP 2 | #define UTIL_TEXT_HPP 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | #include 7 | 8 | #include "Core/Drawable.hpp" 9 | #include "Core/Program.hpp" 10 | #include "Core/Texture.hpp" 11 | #include "Core/UniformBuffer.hpp" 12 | #include "Core/VertexArray.hpp" 13 | 14 | #include "Util/Color.hpp" 15 | 16 | namespace Util { 17 | /** 18 | * @class Text 19 | * @brief A class representing a text. 20 | * 21 | * This class encapsulates the properties and behaviors of a text. 22 | * It includes properties such as texture and surface. 23 | * It also includes behaviors such as drawing the text. 24 | */ 25 | class Text : public Core::Drawable { 26 | public: 27 | /** 28 | * @brief Constructor for the Text class. 29 | * 30 | * @param font The font file path or name. 31 | * @param size The font size. 32 | * @param text The text content to render. 33 | * @param color The color of the text (default is gray). 34 | * @param useAA Flag indicating whether anti-aliasing should be enabled 35 | * (default is true). 36 | */ 37 | Text(const std::string &font, int size, const std::string &text, 38 | const Util::Color &color = Color(127, 127, 127), bool useAA = true); 39 | 40 | glm::vec2 GetSize() const override { return m_Size; }; 41 | 42 | /** 43 | * @brief Sets the text to the specified string. 44 | * 45 | * @param text The string to set. 46 | */ 47 | void SetText(const std::string &text) { 48 | m_Text = text; 49 | ApplyTexture(); 50 | } 51 | 52 | /** 53 | * @brief Sets the color of the text. 54 | * 55 | * @param color The color to set. 56 | */ 57 | void SetColor(const Util::Color &color) { 58 | m_Color = color; 59 | ApplyTexture(); 60 | }; 61 | 62 | /** 63 | * @brief Sets whether anti-aliasing (AA) should be enabled or disabled. 64 | * 65 | * @param useAA A boolean value indicating whether anti-aliasing should be 66 | * enabled (true) or disabled (false). 67 | * 68 | * @note This function only sets the internal flag for anti-aliasing and 69 | * does not directly affect rendering. The actual effect of anti-aliasing 70 | * depends on the rendering pipeline and the graphics hardware capabilities. 71 | * 72 | * @sa https://en.wikipedia.org/wiki/Spatial_anti-aliasing 73 | */ 74 | void UseAntiAliasing(bool useAA); 75 | 76 | /** 77 | * @brief Draws the text with a given transform and z-index. 78 | * 79 | * This function draws the image at the specified z-index and applies the 80 | * given transform. 81 | * 82 | * @param transform The transform to apply to the text. 83 | * @param zIndex The z-index at which to draw the text. 84 | */ 85 | void Draw(const Core::Matrices &data) override; 86 | 87 | private: 88 | void InitProgram(); 89 | void InitVertexArray(); 90 | void InitUniformBuffer(); 91 | 92 | /** 93 | * @brief Applies the texture to the text. 94 | * @note The user should call this function after updating the texture in 95 | * case of custom methods/functions are used. 96 | */ 97 | void ApplyTexture(); 98 | 99 | static constexpr int UNIFORM_SURFACE_LOCATION = 0; 100 | 101 | static std::unique_ptr s_Program; 102 | static std::unique_ptr s_VertexArray; 103 | std::unique_ptr> m_UniformBuffer; 104 | 105 | private: 106 | std::unique_ptr m_Texture = nullptr; 107 | std::unique_ptr> m_Font; 108 | 109 | std::string m_Text; 110 | Util::Color m_Color; 111 | glm::vec2 m_Size; 112 | }; 113 | } // namespace Util 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /include/Util/Time.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_TIME_H 2 | #define UTIL_TIME_H 3 | 4 | #include "pch.hpp" // IWYU pragma: export 5 | 6 | namespace Util { 7 | 8 | using sdl_count_t = Uint64; 9 | using second_t = float; 10 | using ms_t = float; 11 | 12 | /** 13 | * @class Time 14 | * @brief A singleton class that provides time-related functionalities. 15 | * 16 | * This class provides functionalities such as getting the delta time between 17 | * frames. 18 | * 19 | * @note It is designed as a singleton, meaning only one instance of this class 20 | * should exist. Therefore, the user should NOT create their own `Time` object. 21 | */ 22 | class Time { 23 | public: 24 | /** 25 | * @brief Get the delta time between frames in seconds. 26 | * 27 | * This function returns the time difference between the current frame and 28 | * the last frame. The time difference is measured in seconds. 29 | * 30 | * @return The delta time between frames in seconds. 31 | */ 32 | [[deprecated("Use GetDeltaTimeMs() instead.")]] 33 | static second_t GetDeltaTime() { 34 | return s_DeltaTime / 1000.0F; 35 | } 36 | 37 | /** 38 | * @brief Get the delta time between frames in milliseconds. 39 | * 40 | * This function returns the time difference between the current frame and 41 | * the last frame. The time difference is measured in milliseconds. 42 | * 43 | * @return The delta time between frames in milliseconds. 44 | */ 45 | static ms_t GetDeltaTimeMs() { return s_DeltaTime; } 46 | 47 | /** 48 | * @brief Get the elapsed time from the start of the program in 49 | * milliseconds. 50 | * 51 | * @note To create a timer, one may call this function to record the time, 52 | * and then call it again to obtain the time difference. 53 | * 54 | * @return The elapsed time from the start of the program in milliseconds. 55 | */ 56 | static ms_t GetElapsedTimeMs(); 57 | 58 | /** 59 | * @brief Update the time. 60 | * 61 | * This function updates the current time and the delta time. 62 | * It is called by Core::Context::Update(). The user is not recommended to 63 | * call this function. 64 | */ 65 | static void Update(); 66 | 67 | private: 68 | static sdl_count_t s_Start; 69 | 70 | /** 71 | * @brief The current time. 72 | * 73 | * This variable stores the current time. 74 | */ 75 | static sdl_count_t s_Now; 76 | 77 | /** 78 | * @brief The time of the last frame. 79 | * 80 | * This variable stores the time of the last frame. 81 | */ 82 | static sdl_count_t s_Last; 83 | 84 | /** 85 | * @brief The delta time between frames. 86 | * 87 | * This variable stores the time difference between the current frame and 88 | * the last frame. 89 | */ 90 | static ms_t s_DeltaTime; 91 | }; 92 | } // namespace Util 93 | 94 | #endif /* UTIL_TIME_H */ 95 | -------------------------------------------------------------------------------- /include/Util/Transform.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_TRANSFORM_HPP 2 | #define UTIL_TRANSFORM_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace Util { 8 | 9 | /** 10 | * @struct Transform 11 | * @brief A struct representing a transformation in 2D space. 12 | * 13 | * This struct encapsulates the properties of a transformation in 2D space. 14 | * @note The order of Util::Transform is translation, rotation, and scale. 15 | */ 16 | struct Transform { 17 | /** 18 | * @brief The translation of the transformation. 19 | * 20 | * This property represents the translation of the transformation in 2D 21 | * space. It is represented as a 2D vector. 22 | */ 23 | glm::vec2 translation = {0, 0}; 24 | 25 | /** 26 | * @brief The rotation of the transformation. 27 | * 28 | * This property represents the rotation of the transformation in 2D space. 29 | * It is represented as a float. 30 | * @note The unit of rotation is in radians not degrees. 31 | */ 32 | float rotation = 0; 33 | 34 | /** 35 | * @brief The scale of the transformation. 36 | * 37 | * This property represents the scale of the transformation in 2D space. 38 | * It is represented as a 2D vector. 39 | */ 40 | glm::vec2 scale = {1, 1}; 41 | }; 42 | 43 | } // namespace Util 44 | 45 | #endif /* UTIL_TRANSFORM_HPP */ 46 | -------------------------------------------------------------------------------- /include/Util/TransformUtils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_TRANSFORM_UTILS_HPP 2 | #define UTIL_TRANSFORM_UTILS_HPP 3 | 4 | #include "Core/Drawable.hpp" 5 | 6 | #include "Util/Transform.hpp" 7 | #include "pch.hpp" 8 | 9 | namespace Util { 10 | 11 | /** 12 | * @brief Converts a Transform object into uniform buffer data. 13 | * 14 | * Converts transform data in Core::UniformBuffer format. 15 | * Call it and pass the returned value to Core::UniformBuffer->setData(). 16 | * 17 | * @param transform The Transform object to be converted. 18 | * @param size The size of the object. 19 | * @param zIndex The z-index of the transformation. 20 | * @return A Matrices object representing the uniform buffer data. 21 | * 22 | */ 23 | Core::Matrices ConvertToUniformBufferData(const Util::Transform &transform, 24 | const glm::vec2 &size, float zIndex); 25 | 26 | } // namespace Util 27 | 28 | #endif // UTIL_TRANSFORM_UTILS_HPP 29 | -------------------------------------------------------------------------------- /include/config.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #include "Util/Logger.hpp" 5 | #include "pch.hpp" // IWYU pragma: export 6 | 7 | namespace Core { 8 | class Context; 9 | } 10 | 11 | // clang-format off 12 | /** 13 | * To use, place a config.json file either in the current directory (.) or 14 | * the parent directory (..). If both directories contain the file, the one 15 | * in the current directory will be read. 16 | * 17 | * Example directory structure: 18 | * 19 | * @code{.md} 20 | * ├── build/ 21 | * │ ├── YOUR_GAME 22 | * │ └── config.json <- place here 23 | * └── config.json <- or here 24 | * @endcode 25 | * 26 | * The config.json file can contain any number of configurations, each in 27 | * the following format: 28 | * 29 | * @code{.json} 30 | * { 31 | * "title": "", 32 | * "window_pos_x": , 33 | * "window_pos_y": , 34 | * "window_width": , 35 | * "window_height": , 36 | * "default_log_level": , 37 | * "fps_cap": 38 | * } 39 | * @endcode 40 | * 41 | * If a key is not present in the config.json, the default value will be 42 | * used. 43 | */ 44 | // clang-format on 45 | struct PTSD_Config { 46 | public: 47 | static std::string TITLE; 48 | 49 | static int WINDOW_POS_X; 50 | 51 | static int WINDOW_POS_Y; 52 | static unsigned int WINDOW_WIDTH; 53 | 54 | static unsigned int WINDOW_HEIGHT; 55 | static Util::Logger::Level DEFAULT_LOG_LEVEL; 56 | 57 | /** 58 | * @brief FPS limit 59 | * 60 | * Set value to 0 to turn off FPS cap 61 | */ 62 | static unsigned int FPS_CAP; 63 | 64 | private: 65 | friend class Core::Context; 66 | static void Init(); 67 | }; // namespace PTSD_Config 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /include/pch.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PCH_HPP 2 | #define PCH_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #define GLM_ENABLE_EXPERIMENTAL 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/Core/Context.cpp: -------------------------------------------------------------------------------- 1 | #include "Core/Context.hpp" 2 | 3 | #include 4 | 5 | #include "Core/DebugMessageCallback.hpp" 6 | 7 | #include "Util/Input.hpp" 8 | #include "Util/Logger.hpp" 9 | #include "Util/Time.hpp" 10 | 11 | #include "config.hpp" 12 | 13 | using Util::ms_t; 14 | 15 | namespace Core { 16 | Context::Context() { 17 | Util::Logger::Init(); 18 | PTSD_Config::Init(); 19 | Util::Logger::SetLevel(PTSD_Config::DEFAULT_LOG_LEVEL); 20 | 21 | if (SDL_Init(SDL_INIT_VIDEO) < 0) { 22 | LOG_ERROR("Failed to initialize SDL"); 23 | LOG_ERROR(SDL_GetError()); 24 | } 25 | 26 | if (IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG) < 0) { 27 | LOG_ERROR("Failed to initialize SDL_image"); 28 | LOG_ERROR(SDL_GetError()); 29 | } 30 | 31 | if (TTF_Init() < 0) { 32 | LOG_ERROR("Failed to initialize SDL_ttf"); 33 | LOG_ERROR(SDL_GetError()); 34 | } 35 | 36 | if (Mix_Init(MIX_INIT_MP3) < 0) { 37 | LOG_ERROR("Failed to initialize SDL_mixer"); 38 | LOG_ERROR(SDL_GetError()); 39 | } 40 | 41 | if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) { 42 | LOG_ERROR("Failed to initialize SDL_mixer"); 43 | LOG_ERROR(SDL_GetError()); 44 | } 45 | m_Window = SDL_CreateWindow( 46 | PTSD_Config::TITLE.c_str(), PTSD_Config::WINDOW_POS_X, 47 | PTSD_Config::WINDOW_POS_Y, PTSD_Config::WINDOW_WIDTH, 48 | PTSD_Config::WINDOW_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN); 49 | 50 | if (m_Window == nullptr) { 51 | LOG_ERROR("Failed to create window"); 52 | LOG_ERROR(SDL_GetError()); 53 | } 54 | 55 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 56 | SDL_GL_CONTEXT_PROFILE_CORE); 57 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 58 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); 59 | 60 | m_GlContext = SDL_GL_CreateContext(m_Window); 61 | 62 | if (m_GlContext == nullptr) { 63 | LOG_ERROR("Failed to initialize GL context"); 64 | LOG_ERROR(SDL_GetError()); 65 | } 66 | 67 | glewExperimental = GL_TRUE; 68 | if (glewInit() != GLEW_OK) { 69 | GLuint err = glGetError(); 70 | LOG_ERROR(reinterpret_cast(glewGetErrorString(err))); 71 | } 72 | 73 | #ifndef __APPLE__ 74 | glEnable(GL_DEBUG_OUTPUT); 75 | glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); 76 | glDebugMessageCallback(Core::OpenGLDebugMessageCallback, nullptr); 77 | #endif 78 | 79 | glEnable(GL_DEPTH_TEST); 80 | glEnable(GL_BLEND); 81 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 82 | 83 | LOG_INFO("OpenGL Info"); 84 | LOG_INFO(" Vendor: {}", glGetString(GL_VENDOR)); 85 | LOG_INFO(" Renderer: {}", glGetString(GL_RENDERER)); 86 | LOG_INFO(" Version: {}", glGetString(GL_VERSION)); 87 | LOG_INFO(" GLSL Version: {}", glGetString(GL_SHADING_LANGUAGE_VERSION)); 88 | 89 | IMGUI_CHECKVERSION(); 90 | ImGui::CreateContext(); 91 | ImGuiIO &io = ImGui::GetIO(); 92 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; 93 | io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; 94 | io.ConfigWindowsMoveFromTitleBarOnly = true; 95 | 96 | ImGui_ImplSDL2_InitForOpenGL(m_Window, m_GlContext); 97 | ImGui_ImplOpenGL3_Init(); 98 | } 99 | std::shared_ptr Context::s_Instance(nullptr); 100 | 101 | Context::~Context() { 102 | ImGui_ImplOpenGL3_Shutdown(); 103 | ImGui_ImplSDL2_Shutdown(); 104 | ImGui::DestroyContext(); 105 | 106 | SDL_DestroyWindow(m_Window); 107 | SDL_GL_DeleteContext(m_GlContext); 108 | SDL_VideoQuit(); 109 | Mix_HaltGroup(-1); 110 | Mix_CloseAudio(); 111 | 112 | TTF_Quit(); 113 | IMG_Quit(); 114 | Mix_Quit(); 115 | SDL_Quit(); 116 | } 117 | 118 | void Context::Setup() { 119 | ImGui_ImplOpenGL3_NewFrame(); 120 | ImGui_ImplSDL2_NewFrame(); 121 | ImGui::NewFrame(); 122 | } 123 | 124 | void Context::Update() { 125 | Util::Input::Update(); 126 | SDL_GL_SwapWindow(m_Window); 127 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 128 | 129 | static ms_t frameTime = 130 | PTSD_Config::FPS_CAP != 0 ? 1000.0F / PTSD_Config::FPS_CAP : 0; 131 | ms_t afterUpdate = Util::Time::GetElapsedTimeMs(); 132 | ms_t updateTime = afterUpdate - m_BeforeUpdateTime; 133 | if (updateTime < frameTime) { 134 | SDL_Delay(static_cast(frameTime - updateTime)); 135 | } 136 | m_BeforeUpdateTime = Util::Time::GetElapsedTimeMs(); 137 | 138 | // Here's a figure explaining how Delta time & Delay work: 139 | // 140 | // --|--UT--|--Delay--|--UT--|-- 141 | // |---Delta time---| ^ Last delta time used here 142 | // ^ ^ 143 | // (s_Last) (s_Now) Time::Update here 144 | // 145 | // # Updating/rendering time is denoted as "UT" 146 | Util::Time::Update(); 147 | 148 | #ifdef DEBUG_DELTA_TIME 149 | auto deltaTime = Util::Time::GetDeltaTimeMs(); 150 | LOG_DEBUG("Delta(Update+Delay): {:.1f}({:.1f}+{:.1f}) ms, FPS: {:.1f}", 151 | deltaTime, updateTime, 152 | updateTime < frameTime ? frameTime - updateTime : 0, 153 | 1000.0f / deltaTime); 154 | #endif // DEBUG_DELTA_TIME 155 | } 156 | 157 | std::shared_ptr Context::GetInstance() { 158 | if (s_Instance == nullptr) { 159 | s_Instance = std::make_shared(); 160 | } 161 | return s_Instance; 162 | } 163 | 164 | void Context::SetWindowIcon(const std::string &path) { 165 | SDL_Surface *image = IMG_Load(path.c_str()); 166 | if (image) { 167 | SDL_SetWindowIcon(m_Window, image); 168 | SDL_FreeSurface(image); 169 | return; 170 | } 171 | LOG_ERROR("Failed to load image: {}", path); 172 | } 173 | } // namespace Core 174 | -------------------------------------------------------------------------------- /src/Core/DebugMessageCallback.cpp: -------------------------------------------------------------------------------- 1 | #include "Core/DebugMessageCallback.hpp" 2 | 3 | #include "Util/Logger.hpp" 4 | 5 | namespace Core { 6 | void GLAPIENTRY OpenGLDebugMessageCallback(GLenum source, GLenum type, 7 | GLuint id, // NOLINT 8 | GLenum severity, GLsizei length, 9 | const GLchar *message, 10 | const void *data) { 11 | std::string sourceString; 12 | std::string typeString; 13 | std::string severityString; 14 | 15 | // clang-format off 16 | switch (source) { 17 | case GL_DEBUG_SOURCE_API: sourceString = "API"; break; 18 | case GL_DEBUG_SOURCE_WINDOW_SYSTEM: sourceString = "WINDOW SYSTEM"; break; 19 | case GL_DEBUG_SOURCE_SHADER_COMPILER: sourceString = "SHADER COMPILER"; break; 20 | case GL_DEBUG_SOURCE_THIRD_PARTY: sourceString = "THIRD PARTY"; break; 21 | case GL_DEBUG_SOURCE_APPLICATION: sourceString = "APPLICATION"; break; 22 | default: sourceString = "UNKNOWN"; break; 23 | } 24 | 25 | switch (type) { 26 | case GL_DEBUG_TYPE_ERROR: typeString = "ERROR"; break; 27 | case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: typeString = "DEPRECATED BEHAVIOR"; break; 28 | case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: typeString = "UNDEFINED BEHAVIOR"; break; 29 | case GL_DEBUG_TYPE_PORTABILITY: typeString = "PORTABILITY"; break; 30 | case GL_DEBUG_TYPE_PERFORMANCE: typeString = "PERFORMANCE"; break; 31 | case GL_DEBUG_TYPE_OTHER: typeString = "OTHER"; break; 32 | case GL_DEBUG_TYPE_MARKER: typeString = "MARKER"; break; 33 | default: typeString = "UNKNOWN"; break; 34 | } 35 | 36 | switch (severity) { 37 | case GL_DEBUG_SEVERITY_HIGH: severityString = "HIGH"; break; 38 | case GL_DEBUG_SEVERITY_MEDIUM: severityString = "MEDIUM"; break; 39 | case GL_DEBUG_SEVERITY_LOW: severityString = "LOW"; break; 40 | case GL_DEBUG_SEVERITY_NOTIFICATION: severityString = "NOTIFICATION"; break; 41 | default: severityString = "UNKNOWN"; break; 42 | } 43 | // clang-format on 44 | 45 | switch (severity) { 46 | case GL_DEBUG_SEVERITY_HIGH: 47 | LOG_ERROR("OpenGL Severity: {}", severityString); 48 | LOG_ERROR(" ID: {}", id); 49 | LOG_ERROR(" Source: {}", sourceString); 50 | LOG_ERROR(" Type: {}", typeString); 51 | LOG_ERROR(" Message: {}", message); 52 | break; 53 | 54 | case GL_DEBUG_SEVERITY_MEDIUM: 55 | LOG_WARN("OpenGL Severity: {}", severityString); 56 | LOG_WARN(" ID: {}", id); 57 | LOG_WARN(" Source: {}", sourceString); 58 | LOG_WARN(" Type: {}", typeString); 59 | LOG_WARN(" Message: {}", message); 60 | break; 61 | 62 | case GL_DEBUG_SEVERITY_LOW: 63 | LOG_INFO("OpenGL Severity {}", severityString); 64 | LOG_INFO(" ID: {}", id); 65 | LOG_INFO(" Source: {}", sourceString); 66 | LOG_INFO(" Type: {}", typeString); 67 | LOG_INFO(" Message: {}", message); 68 | break; 69 | 70 | case GL_DEBUG_SEVERITY_NOTIFICATION: 71 | LOG_DEBUG("OpenGL Severity {}", severityString); 72 | LOG_DEBUG(" ID: {}", id); 73 | LOG_DEBUG(" Source: {}", sourceString); 74 | LOG_DEBUG(" Type: {}", typeString); 75 | LOG_DEBUG(" Message: {}", message); 76 | break; 77 | 78 | default: 79 | LOG_DEBUG("OpenGL Severity Unknown"); 80 | LOG_DEBUG(" ID: {}", id); 81 | LOG_DEBUG(" Source: {}", sourceString); 82 | LOG_DEBUG(" Type: {}", typeString); 83 | LOG_DEBUG(" Message: {}", message); 84 | break; 85 | } 86 | 87 | // unused variable, this is for silencing warnings 88 | (void)length; 89 | (void)data; 90 | } 91 | } // namespace Core 92 | -------------------------------------------------------------------------------- /src/Core/IndexBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "Core/IndexBuffer.hpp" 2 | 3 | namespace Core { 4 | IndexBuffer::IndexBuffer(const std::vector &indices) 5 | : m_Count(indices.size()) { 6 | glGenBuffers(1, &m_BufferId); 7 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_BufferId); 8 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, 9 | static_cast(indices.size() * sizeof(GLuint)), 10 | indices.data(), GL_STATIC_DRAW); 11 | } 12 | 13 | IndexBuffer::IndexBuffer(IndexBuffer &&other) { 14 | m_BufferId = other.m_BufferId; 15 | other.m_BufferId = 0; 16 | 17 | m_Count = std::move(other.m_Count); 18 | } 19 | 20 | IndexBuffer::~IndexBuffer() { 21 | glDeleteBuffers(1, &m_BufferId); 22 | } 23 | 24 | IndexBuffer &IndexBuffer::operator=(IndexBuffer &&other) { 25 | m_BufferId = other.m_BufferId; 26 | other.m_BufferId = 0; 27 | 28 | m_Count = std::move(other.m_Count); 29 | 30 | return *this; 31 | } 32 | 33 | void IndexBuffer::Bind() const { 34 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_BufferId); 35 | } 36 | 37 | void IndexBuffer::Unbind() const { 38 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 39 | } 40 | } // namespace Core 41 | -------------------------------------------------------------------------------- /src/Core/Program.cpp: -------------------------------------------------------------------------------- 1 | #include "Core/Program.hpp" 2 | 3 | #include "Core/Shader.hpp" 4 | 5 | #include "Util/Logger.hpp" 6 | 7 | namespace Core { 8 | Program::Program(const std::string &vertexShaderFilepath, 9 | const std::string &fragmentShaderFilepath) { 10 | m_ProgramId = glCreateProgram(); 11 | 12 | Shader vertex(vertexShaderFilepath, Shader::Type::VERTEX); 13 | Shader fragment(fragmentShaderFilepath, Shader::Type::FRAGMENT); 14 | 15 | glAttachShader(m_ProgramId, vertex.GetShaderId()); 16 | glAttachShader(m_ProgramId, fragment.GetShaderId()); 17 | 18 | glLinkProgram(m_ProgramId); 19 | 20 | CheckStatus(); 21 | 22 | glDetachShader(m_ProgramId, vertex.GetShaderId()); 23 | glDetachShader(m_ProgramId, fragment.GetShaderId()); 24 | } 25 | 26 | Program::Program(Program &&other) { 27 | m_ProgramId = other.m_ProgramId; 28 | other.m_ProgramId = 0; 29 | } 30 | 31 | Program::~Program() { 32 | glDeleteProgram(m_ProgramId); 33 | } 34 | 35 | Program &Program::operator=(Program &&other) { 36 | m_ProgramId = other.m_ProgramId; 37 | other.m_ProgramId = 0; 38 | 39 | return *this; 40 | } 41 | 42 | void Program::Bind() const { 43 | glUseProgram(m_ProgramId); 44 | } 45 | 46 | void Program::Unbind() const { 47 | glUseProgram(0); 48 | } 49 | 50 | void Program::Validate() const { 51 | GLint status = GL_FALSE; 52 | 53 | glValidateProgram(m_ProgramId); 54 | glGetProgramiv(m_ProgramId, GL_VALIDATE_STATUS, &status); 55 | if (status != GL_TRUE) { 56 | int infoLogLength; 57 | glGetProgramiv(m_ProgramId, GL_INFO_LOG_LENGTH, &infoLogLength); 58 | 59 | std::vector message(infoLogLength + 1); 60 | glGetProgramInfoLog(m_ProgramId, infoLogLength, nullptr, 61 | message.data()); 62 | 63 | LOG_ERROR("Validation Failed:"); 64 | LOG_ERROR("{}", message.data()); 65 | } 66 | } 67 | 68 | void Program::CheckStatus() const { 69 | GLint status = GL_FALSE; 70 | 71 | glGetProgramiv(m_ProgramId, GL_LINK_STATUS, &status); 72 | if (status != GL_TRUE) { 73 | int infoLogLength; 74 | glGetProgramiv(m_ProgramId, GL_INFO_LOG_LENGTH, &infoLogLength); 75 | 76 | std::vector message(infoLogLength + 1); 77 | glGetProgramInfoLog(m_ProgramId, infoLogLength, nullptr, 78 | message.data()); 79 | 80 | LOG_ERROR("Failed to Link Program:"); 81 | LOG_ERROR("{}", message.data()); 82 | } 83 | } 84 | } // namespace Core 85 | -------------------------------------------------------------------------------- /src/Core/Shader.cpp: -------------------------------------------------------------------------------- 1 | #include "Core/Shader.hpp" 2 | 3 | #include "Util/LoadTextFile.hpp" 4 | #include "Util/Logger.hpp" 5 | 6 | namespace Core { 7 | Shader::Shader(const std::string &filepath, Type shaderType) { 8 | m_ShaderId = glCreateShader(static_cast(shaderType)); 9 | 10 | Compile(Util::LoadTextFile(filepath)); 11 | CheckStatus(filepath); 12 | } 13 | 14 | Shader::Shader(Shader &&other) { 15 | m_ShaderId = other.m_ShaderId; 16 | other.m_ShaderId = 0; 17 | } 18 | 19 | Shader::~Shader() { 20 | glDeleteShader(m_ShaderId); 21 | } 22 | 23 | Shader &Shader::operator=(Shader &&other) { 24 | m_ShaderId = other.m_ShaderId; 25 | other.m_ShaderId = 0; 26 | 27 | return *this; 28 | } 29 | 30 | void Shader::Compile(const std::string &src) const { 31 | const char *srcPtr = src.c_str(); 32 | 33 | glShaderSource(m_ShaderId, 1, &srcPtr, nullptr); 34 | glCompileShader(m_ShaderId); 35 | } 36 | 37 | void Shader::CheckStatus(const std::string &filepath) const { 38 | GLint status = GL_FALSE; 39 | 40 | glGetShaderiv(m_ShaderId, GL_COMPILE_STATUS, &status); 41 | if (status != GL_TRUE) { 42 | int infoLogLength; 43 | glGetShaderiv(m_ShaderId, GL_INFO_LOG_LENGTH, &infoLogLength); 44 | 45 | std::vector message(infoLogLength + 1); 46 | glGetShaderInfoLog(m_ShaderId, infoLogLength, nullptr, message.data()); 47 | 48 | LOG_ERROR("Failed to Compile Shader: '{}'"); 49 | LOG_ERROR("{}", filepath, message.data()); 50 | } 51 | } 52 | } // namespace Core 53 | -------------------------------------------------------------------------------- /src/Core/Texture.cpp: -------------------------------------------------------------------------------- 1 | #include "Core/Texture.hpp" 2 | 3 | #include "Core/TextureUtils.hpp" 4 | 5 | #include "Util/Logger.hpp" 6 | 7 | namespace Core { 8 | Texture::Texture(GLint format, int width, int height, const void *data, 9 | bool useAA) { 10 | glGenTextures(1, &m_TextureId); 11 | UseAntiAliasing(useAA); 12 | UpdateData(format, width, height, data); 13 | } 14 | 15 | Texture::Texture(Texture &&texture) { 16 | m_TextureId = texture.m_TextureId; 17 | texture.m_TextureId = 0; 18 | } 19 | 20 | Texture::~Texture() { 21 | glDeleteTextures(1, &m_TextureId); 22 | } 23 | 24 | Texture &Texture::operator=(Texture &&other) { 25 | m_TextureId = other.m_TextureId; 26 | other.m_TextureId = 0; 27 | return *this; 28 | } 29 | 30 | void Texture::Bind(int slot) const { 31 | int maxCount; 32 | glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxCount); 33 | 34 | if (slot >= maxCount) { 35 | LOG_ERROR("Maximum texture count exceeded"); 36 | return; 37 | } 38 | 39 | glActiveTexture(GL_TEXTURE0 + slot); 40 | glBindTexture(GL_TEXTURE_2D, m_TextureId); 41 | } 42 | 43 | void Texture::Unbind() const { 44 | glBindTexture(GL_TEXTURE_2D, 0); 45 | } 46 | 47 | /** 48 | * Although this function doesn't modify it's object state, it changes states in 49 | * OpenGL under the hood, so it doesn't make sense to mark it as `const` 50 | */ 51 | // NOLINTNEXTLINE(readability-make-member-function-const) 52 | void Texture::UpdateData(GLint format, int width, int height, 53 | const void *data) { 54 | glBindTexture(GL_TEXTURE_2D, m_TextureId); 55 | 56 | // Reference: 57 | // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml 58 | glTexImage2D(GL_TEXTURE_2D, 0, GlFormatToGlInternalFormat(format), width, 59 | height, 0, format, GL_UNSIGNED_BYTE, data); 60 | 61 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); 62 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); 63 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_MinFilter); 64 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_MagFilter); 65 | glGenerateMipmap(GL_TEXTURE_2D); 66 | } 67 | 68 | void Texture::UseAntiAliasing(bool useAA) { 69 | /** 70 | * additional docs 71 | * https://www.khronos.org/opengl/wiki/Texture 72 | * https://www.khronos.org/opengl/wiki/Sampler_Object#Sampling_parameters 73 | */ 74 | if (useAA) { 75 | m_MinFilter = GL_LINEAR_MIPMAP_LINEAR; 76 | m_MagFilter = GL_LINEAR; 77 | } else { 78 | m_MinFilter = GL_NEAREST_MIPMAP_NEAREST; 79 | m_MagFilter = GL_NEAREST; 80 | } 81 | } 82 | } // namespace Core 83 | -------------------------------------------------------------------------------- /src/Core/TextureUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "Core/TextureUtils.hpp" 2 | 3 | namespace Core { 4 | GLint SdlFormatToGlFormat(Uint32 format) { 5 | switch (format) { 6 | case SDL_PIXELFORMAT_RGB24: 7 | return GL_RGB; 8 | case SDL_PIXELFORMAT_BGR24: 9 | return GL_BGR; 10 | case SDL_PIXELFORMAT_XRGB8888: 11 | case SDL_PIXELFORMAT_ARGB8888: 12 | return GL_BGRA; 13 | case SDL_PIXELFORMAT_XBGR8888: 14 | case SDL_PIXELFORMAT_ABGR8888: 15 | return GL_RGBA; 16 | default: 17 | LOG_ERROR("Format currently unsupported: {}", 18 | SDL_GetPixelFormatName(format)); 19 | return -1; 20 | } 21 | } 22 | 23 | GLint GlFormatToGlInternalFormat(GLint format) { 24 | switch (format) { 25 | case GL_RGB: 26 | case GL_BGR: 27 | return GL_RGB16; 28 | case GL_RGBA: 29 | case GL_BGRA: 30 | return GL_RGBA16; 31 | default: 32 | LOG_ERROR("Format currently unsupported"); 33 | return -1; 34 | } 35 | } 36 | } // namespace Core 37 | -------------------------------------------------------------------------------- /src/Core/VertexArray.cpp: -------------------------------------------------------------------------------- 1 | #include "Core/VertexArray.hpp" 2 | 3 | namespace Core { 4 | VertexArray::VertexArray() { 5 | glGenVertexArrays(1, &m_ArrayId); 6 | } 7 | 8 | VertexArray::VertexArray(VertexArray &&other) { 9 | m_ArrayId = other.m_ArrayId; 10 | other.m_ArrayId = 0; 11 | 12 | m_VertexBuffers = std::move(other.m_VertexBuffers); 13 | m_IndexBuffer = std::move(other.m_IndexBuffer); 14 | } 15 | 16 | VertexArray::~VertexArray() { 17 | glDeleteVertexArrays(1, &m_ArrayId); 18 | } 19 | 20 | VertexArray &VertexArray::operator=(VertexArray &&other) { 21 | m_ArrayId = other.m_ArrayId; 22 | other.m_ArrayId = 0; 23 | 24 | m_VertexBuffers = std::move(other.m_VertexBuffers); 25 | m_IndexBuffer = std::move(other.m_IndexBuffer); 26 | 27 | return *this; 28 | } 29 | 30 | void VertexArray::Bind() const { 31 | glBindVertexArray(m_ArrayId); 32 | } 33 | 34 | void VertexArray::Unbind() const { 35 | glBindVertexArray(0); 36 | } 37 | 38 | void VertexArray::AddVertexBuffer(std::unique_ptr vertexBuffer) { 39 | glBindVertexArray(m_ArrayId); 40 | 41 | glEnableVertexAttribArray(m_VertexBuffers.size()); 42 | vertexBuffer->Bind(); 43 | 44 | glVertexAttribPointer(m_VertexBuffers.size(), 45 | static_cast(vertexBuffer->GetComponentCount()), 46 | vertexBuffer->GetType(), GL_FALSE, 0, nullptr); 47 | 48 | m_VertexBuffers.push_back(std::move(vertexBuffer)); 49 | } 50 | 51 | void VertexArray::SetIndexBuffer(std::unique_ptr indexBuffer) { 52 | m_IndexBuffer = std::move(indexBuffer); 53 | } 54 | 55 | void VertexArray::DrawTriangles() const { 56 | glDrawElements(GL_TRIANGLES, static_cast(m_IndexBuffer->GetCount()), 57 | GL_UNSIGNED_INT, nullptr); 58 | } 59 | } // namespace Core 60 | -------------------------------------------------------------------------------- /src/Core/VertexBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "Core/VertexBuffer.hpp" 2 | 3 | namespace Core { 4 | VertexBuffer::VertexBuffer(const std::vector &vertices, 5 | unsigned int componentCount) 6 | : m_ComponentCount(componentCount) { 7 | glGenBuffers(1, &m_BufferId); 8 | glBindBuffer(GL_ARRAY_BUFFER, m_BufferId); 9 | glBufferData(GL_ARRAY_BUFFER, 10 | static_cast(vertices.size() * sizeof(GLfloat)), 11 | vertices.data(), GL_STATIC_DRAW); 12 | } 13 | 14 | VertexBuffer::VertexBuffer(VertexBuffer &&other) { 15 | m_BufferId = other.m_BufferId; 16 | other.m_BufferId = 0; 17 | 18 | m_ComponentCount = std::move(other.m_ComponentCount); 19 | m_Type = std::move(other.m_Type); 20 | } 21 | 22 | VertexBuffer::~VertexBuffer() { 23 | glDeleteBuffers(1, &m_BufferId); 24 | } 25 | 26 | VertexBuffer &VertexBuffer::operator=(VertexBuffer &&other) { 27 | m_BufferId = other.m_BufferId; 28 | other.m_BufferId = 0; 29 | 30 | m_ComponentCount = std::move(other.m_ComponentCount); 31 | m_Type = std::move(other.m_Type); 32 | 33 | return *this; 34 | } 35 | 36 | void VertexBuffer::Bind() const { 37 | glBindBuffer(GL_ARRAY_BUFFER, m_BufferId); 38 | } 39 | 40 | void VertexBuffer::Unbind() const { 41 | glBindBuffer(GL_ARRAY_BUFFER, 0); 42 | } 43 | } // namespace Core 44 | -------------------------------------------------------------------------------- /src/Util/Animation.cpp: -------------------------------------------------------------------------------- 1 | #include "Util/Animation.hpp" 2 | #include "Util/Logger.hpp" 3 | #include "Util/Time.hpp" 4 | 5 | namespace Util { 6 | Animation::Animation(const std::vector &paths, bool play, 7 | std::size_t interval, bool looping, std::size_t cooldown, 8 | bool useAA) 9 | : m_State(play ? State::PLAY : State::PAUSE), 10 | m_Interval(interval), 11 | m_Looping(looping), 12 | m_Cooldown(cooldown) { 13 | m_Frames.reserve(paths.size()); 14 | for (const auto &path : paths) { 15 | m_Frames.push_back(std::make_shared(path, useAA)); 16 | } 17 | } 18 | 19 | void Animation::UseAntiAliasing(bool useAA) { 20 | for (const auto &frame : m_Frames) { 21 | frame->UseAntiAliasing(useAA); 22 | } 23 | } 24 | 25 | void Animation::SetCurrentFrame(std::size_t index) { 26 | m_Index = index; 27 | if (m_State == State::ENDED || m_State == State::COOLDOWN) { 28 | /*this make sure if user setframe on ENDED/COOLDOWN, will play from 29 | * where you set the frame*/ 30 | m_IsChangeFrame = true; 31 | } 32 | } 33 | 34 | void Animation::Draw(const Core::Matrices &data) { 35 | m_Frames[m_Index]->Draw(data); 36 | Update(); 37 | } 38 | 39 | void Animation::Play() { 40 | if (m_State == State::PLAY) 41 | return; 42 | if (m_State == State::ENDED || m_State == State::COOLDOWN) { 43 | m_Index = m_IsChangeFrame ? m_Index : 0; 44 | m_IsChangeFrame = false; 45 | } 46 | m_State = State::PLAY; 47 | } 48 | 49 | void Animation::Pause() { 50 | if (m_State == State::PLAY || m_State == State::COOLDOWN) { 51 | m_State = State::PAUSE; 52 | } 53 | } 54 | 55 | void Animation::Update() { 56 | unsigned long nowTime = Util::Time::GetElapsedTimeMs(); 57 | if (m_State == State::PAUSE || m_State == State::ENDED) { 58 | LOG_TRACE("[ANI] is pause"); 59 | return; 60 | } 61 | 62 | if (m_State == State::COOLDOWN) { 63 | if (nowTime >= m_CooldownEndTime) { 64 | Play(); 65 | } 66 | return; 67 | } 68 | 69 | m_TimeBetweenFrameUpdate += Util::Time::GetDeltaTimeMs(); 70 | auto updateFrameCount = 71 | static_cast(m_TimeBetweenFrameUpdate / m_Interval); 72 | if (updateFrameCount <= 0) 73 | return; 74 | 75 | m_Index += updateFrameCount; 76 | m_TimeBetweenFrameUpdate = 0; 77 | 78 | unsigned int const totalFramesCount = m_Frames.size(); 79 | if (m_Index >= totalFramesCount) { 80 | if (m_Looping) { 81 | m_CooldownEndTime = nowTime + m_Cooldown; 82 | } 83 | m_State = m_Looping ? State::COOLDOWN : State::ENDED; 84 | m_Index = m_Frames.size() - 1; 85 | } 86 | }; 87 | } // namespace Util 88 | -------------------------------------------------------------------------------- /src/Util/BGM.cpp: -------------------------------------------------------------------------------- 1 | #include "Util/BGM.hpp" 2 | #include "Util/Logger.hpp" 3 | 4 | std::shared_ptr LoadMusic(const std::string &filepath) { 5 | auto music = std::shared_ptr(Mix_LoadMUS(filepath.c_str()), 6 | Mix_FreeMusic); 7 | 8 | if (music == nullptr) { 9 | LOG_DEBUG("Failed to load BGM: '{}'", filepath); 10 | LOG_DEBUG("{}", Mix_GetError()); 11 | } 12 | 13 | return music; 14 | } 15 | 16 | namespace Util { 17 | 18 | BGM::BGM(const std::string &path) 19 | : m_BGM(s_Store.Get(path)) {} 20 | 21 | int BGM::GetVolume() const { 22 | return Mix_VolumeMusic(-1); 23 | } 24 | 25 | void BGM::SetVolume(const int volume) { 26 | Mix_VolumeMusic(volume); 27 | } 28 | 29 | void BGM::LoadMedia(const std::string &path) { 30 | m_BGM = s_Store.Get(path); 31 | } 32 | 33 | void BGM::VolumeUp(const int step) { 34 | int volume = GetVolume(); 35 | SetVolume(volume + step); 36 | } 37 | 38 | void BGM::VolumeDown(const int step) { 39 | int volume = GetVolume(); 40 | SetVolume(volume - step); 41 | } 42 | 43 | void BGM::Play(const int loop) { 44 | Mix_PlayMusic(m_BGM.get(), loop); 45 | } 46 | 47 | void BGM::FadeIn(const int tick, const int loop) { 48 | Mix_FadeInMusic(m_BGM.get(), loop, tick); 49 | } 50 | 51 | void BGM::FadeOut(const int tick) { 52 | Mix_FadeOutMusic(tick); 53 | } 54 | 55 | void BGM::Pause() { 56 | Mix_PauseMusic(); 57 | } 58 | void BGM::Resume() { 59 | Mix_ResumeMusic(); 60 | } 61 | 62 | Util::AssetStore> BGM::s_Store(LoadMusic); 63 | 64 | } // namespace Util 65 | -------------------------------------------------------------------------------- /src/Util/Color.cpp: -------------------------------------------------------------------------------- 1 | #include "Util/Color.hpp" 2 | #include "Util/Logger.hpp" 3 | 4 | namespace Util { 5 | Color Color::FromRGB(Uint8 r, Uint8 g, Uint8 b, Uint8 a) { 6 | if (r > 255 || g > 255 || b > 255 || a > 255) { 7 | LOG_ERROR("Invalid color: ({}, {}, {}, {})", r, g, b, a); 8 | throw std::invalid_argument("Invalid color"); 9 | } 10 | return Color(r, g, b, a); 11 | } 12 | 13 | Color Color::FromHex(Uint32 hex) { 14 | return FromRGB((hex >> 24) & 0xFF, (hex >> 16) & 0xFF, (hex >> 8) & 0xFF, 15 | hex & 0xFF); 16 | } 17 | 18 | Color Color::FromHex(const std::string &hex) { 19 | try { 20 | Uint32 hexValue = std::stoul(hex, nullptr, 16); 21 | return FromHex(hexValue); 22 | } catch (const std::invalid_argument &e) { 23 | LOG_ERROR("Invalid color hex: '{}'", hex); 24 | throw std::invalid_argument("Invalid hex string"); 25 | } 26 | } 27 | 28 | Color Color::FromName(const Util::Colors &name) { 29 | return FromHex(static_cast(name) << 8 | 0xFF); 30 | } 31 | 32 | Color Color::FromHSL(float h, float s, float l, float a) { 33 | // from https://gist.github.com/ciembor/1494530 34 | auto hueToRgb = [](float p, float q, float t) { 35 | if (t < 0) 36 | t += 1; 37 | if (t > 1) 38 | t -= 1; 39 | if (t < 1.0f / 6.0f) 40 | return p + (q - p) * 6 * t; 41 | if (t < 1.0f / 2.0f) 42 | return q; 43 | if (t < 2.0f / 3.0f) 44 | return p + (q - p) * (2.0f / 3.0f - t) * 6; 45 | return p; 46 | }; 47 | float r, g, b; 48 | if (s == 0) { 49 | r = g = b = l * 255.0; // Achromatic 50 | } else { 51 | float q = l < 0.5f ? l * (1 + s) : l + s - l * s; 52 | float p = 2 * l - q; 53 | r = hueToRgb(p, q, h + 1.0f / 3.0f) * 255; 54 | g = hueToRgb(p, q, h) * 255; 55 | b = hueToRgb(p, q, h - 1.0f / 3.0f) * 255; 56 | } 57 | a *= 255; 58 | return FromRGB(r, g, b, a); 59 | } 60 | 61 | Color Color::FromHSV(float h, float s, float v, float a) { 62 | // from https://stackoverflow.com/a/6930407 63 | float p, q, t, ff, r, g, b; 64 | long i; 65 | v *= 255; 66 | if (s <= 0.0) { 67 | r = v; 68 | g = v; 69 | b = v; 70 | return FromRGB(r, g, b, a); 71 | } 72 | h *= 360.0F; 73 | if (h >= 360.0) 74 | h = 0.0; 75 | h /= 60.0; 76 | i = (long)h; 77 | ff = h - i; 78 | p = v * (1.0 - s); 79 | q = v * (1.0 - (s * ff)); 80 | t = v * (1.0 - (s * (1.0 - ff))); 81 | 82 | switch (i) { 83 | case 0: 84 | r = v; 85 | g = t; 86 | b = p; 87 | break; 88 | case 1: 89 | r = q; 90 | g = v; 91 | b = p; 92 | break; 93 | case 2: 94 | r = p; 95 | g = v; 96 | b = t; 97 | break; 98 | 99 | case 3: 100 | r = p; 101 | g = q; 102 | b = v; 103 | break; 104 | case 4: 105 | r = t; 106 | g = p; 107 | b = v; 108 | break; 109 | case 5: 110 | default: 111 | r = v; 112 | g = p; 113 | b = q; 114 | break; 115 | } 116 | a *= 255; 117 | return FromRGB(static_cast(r), static_cast(g), 118 | static_cast(b), static_cast(a)); 119 | } 120 | } // namespace Util 121 | -------------------------------------------------------------------------------- /src/Util/GameObject.cpp: -------------------------------------------------------------------------------- 1 | #include "Util/GameObject.hpp" 2 | #include "Util/Transform.hpp" 3 | #include "Util/TransformUtils.hpp" 4 | 5 | namespace Util { 6 | 7 | void GameObject::Draw() { 8 | if (!m_Visible || m_Drawable == nullptr) { 9 | return; 10 | } 11 | 12 | auto data = Util::ConvertToUniformBufferData( 13 | m_Transform, m_Drawable->GetSize(), m_ZIndex); 14 | data.m_Model = glm::translate( 15 | data.m_Model, glm::vec3{m_Pivot / m_Drawable->GetSize(), 0} * -1.0F); 16 | 17 | m_Drawable->Draw(data); 18 | } 19 | 20 | } // namespace Util 21 | -------------------------------------------------------------------------------- /src/Util/Image.cpp: -------------------------------------------------------------------------------- 1 | #include "Util/Image.hpp" 2 | 3 | #include "Util/Logger.hpp" 4 | #include "pch.hpp" 5 | 6 | #include "Core/Texture.hpp" 7 | #include "Core/TextureUtils.hpp" 8 | #include "Util/MissingTexture.hpp" 9 | 10 | #include "config.hpp" 11 | #include 12 | 13 | std::shared_ptr LoadSurface(const std::string &filepath) { 14 | auto surface = std::shared_ptr(IMG_Load(filepath.c_str()), 15 | SDL_FreeSurface); 16 | 17 | if (surface == nullptr) { 18 | surface = {Util::GetMissingImageTextureSDLSurface(), SDL_FreeSurface}; 19 | LOG_ERROR("Failed to load image: '{}'", filepath); 20 | LOG_ERROR("{}", IMG_GetError()); 21 | } 22 | 23 | return surface; 24 | } 25 | 26 | namespace Util { 27 | Image::Image(const std::string &filepath, bool useAA) 28 | : m_Path(filepath) { 29 | if (s_Program == nullptr) { 30 | InitProgram(); 31 | } 32 | if (s_VertexArray == nullptr) { 33 | InitVertexArray(); 34 | } 35 | 36 | m_UniformBuffer = std::make_unique>( 37 | *s_Program, "Matrices", 0); 38 | 39 | auto surface = s_Store.Get(filepath); 40 | 41 | if (surface == nullptr) { 42 | LOG_ERROR("Failed to load image: '{}'", filepath); 43 | LOG_ERROR("{}", IMG_GetError()); 44 | surface = {GetMissingImageTextureSDLSurface(), SDL_FreeSurface}; 45 | } 46 | 47 | m_Texture = std::make_unique( 48 | Core::SdlFormatToGlFormat(surface->format->format), surface->w, 49 | surface->h, surface->pixels, useAA); 50 | m_Size = {surface->w, surface->h}; 51 | } 52 | 53 | void Image::SetImage(const std::string &filepath) { 54 | auto surface = s_Store.Get(filepath); 55 | 56 | m_Texture->UpdateData(Core::SdlFormatToGlFormat(surface->format->format), 57 | surface->w, surface->h, surface->pixels); 58 | m_Size = {surface->w, surface->h}; 59 | } 60 | 61 | void Image::UseAntiAliasing(bool useAA) { 62 | m_Texture->UseAntiAliasing(useAA); 63 | } 64 | 65 | void Image::Draw(const Core::Matrices &data) { 66 | m_UniformBuffer->SetData(0, data); 67 | 68 | m_Texture->Bind(UNIFORM_SURFACE_LOCATION); 69 | s_Program->Bind(); 70 | s_Program->Validate(); 71 | 72 | s_VertexArray->Bind(); 73 | s_VertexArray->DrawTriangles(); 74 | } 75 | 76 | void Image::InitProgram() { 77 | // TODO: Create `BaseProgram` from `Program` and pass it into `Drawable` 78 | s_Program = 79 | std::make_unique(PTSD_ASSETS_DIR "/shaders/Base.vert", 80 | PTSD_ASSETS_DIR "/shaders/Base.frag"); 81 | s_Program->Bind(); 82 | 83 | GLint location = glGetUniformLocation(s_Program->GetId(), "surface"); 84 | glUniform1i(location, UNIFORM_SURFACE_LOCATION); 85 | } 86 | 87 | void Image::InitVertexArray() { 88 | s_VertexArray = std::make_unique(); 89 | 90 | // NOLINTBEGIN 91 | // These are vertex data for the rectangle but clang-tidy has magic 92 | // number warnings 93 | 94 | // Vertex 95 | s_VertexArray->AddVertexBuffer(std::make_unique( 96 | std::vector{ 97 | -0.5F, 0.5F, // 98 | -0.5F, -0.5F, // 99 | 0.5F, -0.5F, // 100 | 0.5F, 0.5F, // 101 | }, 102 | 2)); 103 | 104 | // UV 105 | s_VertexArray->AddVertexBuffer(std::make_unique( 106 | std::vector{ 107 | 0.0F, 0.0F, // 108 | 0.0F, 1.0F, // 109 | 1.0F, 1.0F, // 110 | 1.0F, 0.0F, // 111 | }, 112 | 2)); 113 | 114 | // Index 115 | s_VertexArray->SetIndexBuffer( 116 | std::make_unique(std::vector{ 117 | 0, 1, 2, // 118 | 0, 2, 3, // 119 | })); 120 | // NOLINTEND 121 | } 122 | 123 | std::unique_ptr Image::s_Program = nullptr; 124 | std::unique_ptr Image::s_VertexArray = nullptr; 125 | 126 | Util::AssetStore> Image::s_Store(LoadSurface); 127 | } // namespace Util 128 | -------------------------------------------------------------------------------- /src/Util/Input.cpp: -------------------------------------------------------------------------------- 1 | #include "Util/Input.hpp" 2 | 3 | #include // for SDL_Event 4 | 5 | #include "config.hpp" 6 | 7 | namespace Util { 8 | 9 | // init all static members 10 | SDL_Event Input::s_Event = SDL_Event(); 11 | Util::PTSDPosition Input::s_CursorPosition = Util::PTSDPosition(0.0F, 0.0F); 12 | glm::vec2 Input::s_ScrollDistance = glm::vec2(-1.0F, -1.0F); 13 | 14 | std::unordered_map> Input::s_KeyState = { 15 | std::make_pair(Keycode::MOUSE_LB, std::make_pair(false, false)), 16 | std::make_pair(Keycode::MOUSE_RB, std::make_pair(false, false)), 17 | std::make_pair(Keycode::MOUSE_MB, std::make_pair(false, false)), 18 | }; 19 | 20 | bool Input::s_Scroll = false; 21 | bool Input::s_MouseMoving = false; 22 | bool Input::s_Exit = false; 23 | 24 | ImGuiIO Input::s_Io; // allocate memory only because it is invalid 25 | // to call `ImGui::GetIO()` at this time 26 | 27 | bool Input::IsKeyPressed(const Keycode &key) { 28 | 29 | return s_KeyState[key].second; 30 | } 31 | 32 | bool Input::IsKeyDown(const Keycode &key) { 33 | 34 | return s_KeyState[key].second && !s_KeyState[key].first; 35 | } 36 | 37 | bool Input::IsKeyUp(const Keycode &key) { 38 | 39 | return !s_KeyState[key].second && s_KeyState[key].first; 40 | } 41 | 42 | bool Input::IsMouseMoving() { 43 | 44 | return s_MouseMoving; 45 | } 46 | 47 | bool Input::IfScroll() { 48 | 49 | return s_Scroll; 50 | } 51 | 52 | bool Input::IfExit() { 53 | return s_Exit; 54 | } 55 | 56 | glm::vec2 Input::GetScrollDistance() { 57 | 58 | return s_ScrollDistance; 59 | } 60 | 61 | void Input::UpdateKeyState(const SDL_Event *event) { 62 | if (event->type == SDL_MOUSEBUTTONDOWN) { 63 | s_KeyState[static_cast(512 + event->button.button)].second = 64 | true; 65 | } else if (event->type == SDL_MOUSEBUTTONUP) { 66 | s_KeyState[static_cast(512 + event->button.button)].second = 67 | false; 68 | } else if (event->type == SDL_KEYDOWN) { 69 | s_KeyState[static_cast(event->key.keysym.scancode)].second = 70 | true; 71 | } else if (event->type == SDL_KEYUP) { 72 | s_KeyState[static_cast(event->key.keysym.scancode)].second = 73 | false; 74 | } 75 | } 76 | 77 | void Input::Update() { 78 | int x, y; 79 | SDL_GetMouseState(&x, &y); 80 | s_CursorPosition = Util::PTSDPosition::FromSDL(x, y); 81 | 82 | s_Scroll = s_MouseMoving = false; 83 | 84 | for (auto &[_, i] : s_KeyState) { 85 | i.first = i.second; 86 | } 87 | 88 | s_Io = ImGui::GetIO(); 89 | 90 | while (SDL_PollEvent(&s_Event) != 0) { 91 | if (s_Io.WantCaptureMouse) { 92 | ImGui_ImplSDL2_ProcessEvent(&s_Event); 93 | continue; 94 | } 95 | if (s_Event.type == SDL_KEYDOWN || s_Event.type == SDL_KEYUP) { 96 | UpdateKeyState(&s_Event); 97 | } else if (s_Event.type == SDL_MOUSEBUTTONDOWN || 98 | s_Event.type == SDL_MOUSEBUTTONUP) { 99 | UpdateKeyState(&s_Event); 100 | } 101 | 102 | s_Scroll = s_Event.type == SDL_MOUSEWHEEL || s_Scroll; 103 | 104 | if (s_Scroll) { 105 | s_ScrollDistance.x = static_cast(s_Event.wheel.x); 106 | s_ScrollDistance.y = static_cast(s_Event.wheel.y); 107 | } 108 | s_MouseMoving = s_Event.type == SDL_MOUSEMOTION || s_MouseMoving; 109 | s_Exit = s_Event.type == SDL_QUIT; 110 | } 111 | } 112 | 113 | Util::PTSDPosition Input::GetCursorPosition() { 114 | return s_CursorPosition; 115 | } 116 | 117 | void Input::SetCursorPosition(const Util::PTSDPosition &ptsdPos) { 118 | auto sdlPos = ptsdPos.ToSDLPosition(); 119 | SDL_WarpMouseInWindow(nullptr, sdlPos.x, sdlPos.y); 120 | } 121 | 122 | } // namespace Util 123 | -------------------------------------------------------------------------------- /src/Util/LoadTextFile.cpp: -------------------------------------------------------------------------------- 1 | #include "Util/LoadTextFile.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "Util/Logger.hpp" 7 | 8 | namespace Util { 9 | std::string LoadTextFile(const std::string &filepath) { 10 | std::string source; 11 | std::ifstream stream(filepath, std::ios::in); 12 | 13 | LOG_TRACE("Loading File: '{}'", filepath); 14 | 15 | if (stream.is_open()) { 16 | std::stringstream sstr; 17 | sstr << stream.rdbuf(); 18 | source = sstr.str(); 19 | stream.close(); 20 | } else { 21 | LOG_ERROR("Failed to load '{}'", filepath); 22 | } 23 | 24 | return source; 25 | } 26 | } // namespace Util 27 | -------------------------------------------------------------------------------- /src/Util/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Util/Logger.hpp" 2 | 3 | #include "config.hpp" 4 | 5 | namespace Util { 6 | void Logger::Init() { 7 | spdlog::set_pattern("%n [%^%l%$] %v"); 8 | SetLevel(Level::INFO); 9 | } 10 | 11 | void Logger::SetLevel(Logger::Level level) { 12 | spdlog::set_level(static_cast(level)); 13 | } 14 | 15 | Logger::Level GetLevel() { 16 | return static_cast(spdlog::get_level()); 17 | } 18 | } // namespace Util 19 | -------------------------------------------------------------------------------- /src/Util/MissingTexture.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Util/MissingTexture.hpp" 6 | 7 | namespace Util { 8 | SDL_Surface *GetMissingImageTextureSDLSurface() { 9 | SDL_RWops *rwop = 10 | SDL_RWFromConstMem(MISSING_IMAGE_TEXTURE_BASE64_DECODE.data(), 11 | MISSING_IMAGE_TEXTURE_BASE64_DECODE.size()); 12 | SDL_Surface *aSurface = IMG_LoadTyped_RW(rwop, 1, "PNG"); 13 | 14 | if (aSurface == nullptr) { 15 | LOG_ERROR("base64ToSurface"); 16 | } 17 | 18 | return aSurface; 19 | } 20 | 21 | SDL_Surface *GetMissingFontTextureSDLSurface() { 22 | SDL_RWops *rwop = 23 | SDL_RWFromConstMem(MISSING_FONT_TEXTURE_BASE64_DECODE.data(), 24 | MISSING_FONT_TEXTURE_BASE64_DECODE.size()); 25 | SDL_Surface *aSurface = IMG_LoadTyped_RW(rwop, 1, "JPG"); 26 | 27 | if (aSurface == nullptr) { 28 | LOG_ERROR("base64ToSurface"); 29 | } 30 | 31 | return aSurface; 32 | } 33 | } // namespace Util 34 | -------------------------------------------------------------------------------- /src/Util/Position.cpp: -------------------------------------------------------------------------------- 1 | #include "Util/Position.hpp" 2 | #include "config.hpp" 3 | 4 | namespace Util { 5 | PTSDPosition PTSDPosition::operator+(const glm::vec2 vec2) const { 6 | return PTSDPosition(x + vec2.x, y + vec2.y); 7 | } 8 | 9 | PTSDPosition PTSDPosition::FromSDL(int sdlx, int sdly) { 10 | return PTSDPosition{ 11 | static_cast(sdlx) - 12 | static_cast(PTSD_Config::WINDOW_WIDTH) / 2.0F, 13 | -(static_cast(sdly) - 14 | static_cast(PTSD_Config::WINDOW_HEIGHT) / 2.0F)}; 15 | } 16 | 17 | SDLPosition PTSDPosition::ToSDLPosition() const { 18 | return SDLPosition( 19 | static_cast(this->x + 20 | static_cast(PTSD_Config::WINDOW_WIDTH) / 2.0F), 21 | -(static_cast( 22 | this->y + static_cast(PTSD_Config::WINDOW_HEIGHT) / 2.0F))); 23 | } 24 | } // namespace Util 25 | -------------------------------------------------------------------------------- /src/Util/Renderer.cpp: -------------------------------------------------------------------------------- 1 | #include "Util/Renderer.hpp" 2 | 3 | #include 4 | 5 | #include "Util/Logger.hpp" 6 | 7 | namespace Util { 8 | Renderer::Renderer(const std::vector> &children) 9 | : m_Children(children) {} 10 | 11 | void Renderer::AddChild(const std::shared_ptr &child) { 12 | m_Children.push_back(child); 13 | } 14 | 15 | void Renderer::RemoveChild(std::shared_ptr child) { 16 | m_Children.erase(std::remove(m_Children.begin(), m_Children.end(), child), 17 | m_Children.end()); 18 | } 19 | 20 | void Renderer::AddChildren( 21 | const std::vector> &children) { 22 | m_Children.reserve(m_Children.size() + children.size()); 23 | m_Children.insert(m_Children.end(), children.begin(), children.end()); 24 | } 25 | 26 | void Renderer::Update() { 27 | struct StackInfo { 28 | std::shared_ptr m_GameObject; 29 | Transform m_ParentTransform; 30 | }; 31 | 32 | std::vector stack; 33 | stack.reserve(m_Children.size()); 34 | 35 | for (const auto &child : m_Children) { 36 | stack.push_back(StackInfo{child, Transform{}}); 37 | } 38 | 39 | auto compareFunction = [](const StackInfo &a, const StackInfo &b) { 40 | return a.m_GameObject->GetZIndex() > b.m_GameObject->GetZIndex(); 41 | }; 42 | std::priority_queue, 43 | decltype(compareFunction)> 44 | renderQueue(compareFunction); 45 | 46 | while (!stack.empty()) { 47 | auto curr = stack.back(); 48 | stack.pop_back(); 49 | renderQueue.push(curr); 50 | 51 | for (const auto &child : curr.m_GameObject->GetChildren()) { 52 | stack.push_back( 53 | StackInfo{child, curr.m_GameObject->GetTransform()}); 54 | } 55 | } 56 | // draw all in render queue by order 57 | while (!renderQueue.empty()) { 58 | auto curr = renderQueue.top(); 59 | renderQueue.pop(); 60 | 61 | curr.m_GameObject->Draw(); 62 | } 63 | } 64 | } // namespace Util 65 | -------------------------------------------------------------------------------- /src/Util/SFX.cpp: -------------------------------------------------------------------------------- 1 | #include "Util/SFX.hpp" 2 | #include "Util/Logger.hpp" 3 | 4 | std::shared_ptr LoadChunk(const std::string &filepath) { 5 | auto chunk = std::shared_ptr(Mix_LoadWAV(filepath.c_str()), 6 | Mix_FreeChunk); 7 | 8 | if (chunk == nullptr) { 9 | LOG_DEBUG("Failed to load SFX: '{}'", filepath); 10 | LOG_DEBUG("{}", Mix_GetError()); 11 | } 12 | 13 | return chunk; 14 | } 15 | 16 | namespace Util { 17 | 18 | SFX::SFX(const std::string &path) 19 | : m_Chunk(s_Store.Get(path)) {} 20 | 21 | int SFX::GetVolume() const { 22 | return Mix_VolumeChunk(m_Chunk.get(), -1); 23 | } 24 | 25 | void SFX::SetVolume(const int volume) { 26 | Mix_VolumeChunk(m_Chunk.get(), volume); 27 | } 28 | 29 | void SFX::LoadMedia(const std::string &path) { 30 | m_Chunk = s_Store.Get(path); 31 | } 32 | 33 | void SFX::VolumeUp(const int step) { 34 | int volume = GetVolume(); 35 | SetVolume(volume + step); 36 | } 37 | 38 | void SFX::VolumeDown(const int step) { 39 | int volume = GetVolume(); 40 | SetVolume(volume - step); 41 | } 42 | void SFX::Play(const int loop, const int duration) { 43 | Mix_PlayChannelTimed(-1, m_Chunk.get(), loop, duration); 44 | } 45 | void SFX::FadeIn(const unsigned int tick, const int loop, 46 | const unsigned int duration) { 47 | Mix_FadeInChannelTimed(-1, m_Chunk.get(), loop, static_cast(tick), 48 | static_cast(duration)); 49 | } 50 | 51 | Util::AssetStore> SFX::s_Store(LoadChunk); 52 | 53 | } // namespace Util 54 | -------------------------------------------------------------------------------- /src/Util/Text.cpp: -------------------------------------------------------------------------------- 1 | // FIXME: this file should be refactor, API change reference from Image.cpp 2 | 3 | #include "Core/Texture.hpp" 4 | #include "Core/TextureUtils.hpp" 5 | 6 | #include "Util/Logger.hpp" 7 | #include "Util/MissingTexture.hpp" 8 | #include "Util/Text.hpp" 9 | #include "Util/TransformUtils.hpp" 10 | 11 | namespace Util { 12 | Text::Text(const std::string &font, int fontSize, const std::string &text, 13 | const Util::Color &color, bool useAA) 14 | : m_Text(text), 15 | m_Color(color) { 16 | if (s_Program == nullptr) { 17 | InitProgram(); 18 | } 19 | if (s_VertexArray == nullptr) { 20 | InitVertexArray(); 21 | } 22 | 23 | m_UniformBuffer = std::make_unique>( 24 | *s_Program, "Matrices", 0); 25 | 26 | auto surface = 27 | std::unique_ptr>(); 28 | m_Font = {TTF_OpenFont(font.c_str(), fontSize), TTF_CloseFont}; 29 | 30 | if (m_Font == nullptr) { 31 | LOG_ERROR("Failed to load font: '{}'", font.c_str()); 32 | LOG_ERROR("{}", TTF_GetError()); 33 | surface = {GetMissingFontTextureSDLSurface(), SDL_FreeSurface}; 34 | } else { 35 | surface = 36 | std::unique_ptr>{ 37 | TTF_RenderUTF8_Blended_Wrapped(m_Font.get(), m_Text.c_str(), 38 | m_Color.ToSdlColor(), 0), 39 | SDL_FreeSurface, 40 | }; 41 | } 42 | 43 | m_Texture = std::make_unique( 44 | Core::SdlFormatToGlFormat(surface->format->format), 45 | surface->pitch / surface->format->BytesPerPixel, surface->h, 46 | surface->pixels, useAA); 47 | m_Size = {surface->pitch / surface->format->BytesPerPixel, surface->h}; 48 | } 49 | 50 | void Text::UseAntiAliasing(bool useAA) { 51 | m_Texture->UseAntiAliasing(useAA); 52 | } 53 | 54 | void Text::Draw(const Core::Matrices &data) { 55 | m_UniformBuffer->SetData(0, data); 56 | 57 | m_Texture->Bind(UNIFORM_SURFACE_LOCATION); 58 | s_Program->Bind(); 59 | s_Program->Validate(); 60 | 61 | s_VertexArray->Bind(); 62 | s_VertexArray->DrawTriangles(); 63 | } 64 | 65 | void Text::InitProgram() { 66 | // TODO: Create `BaseProgram` from `Program` and pass it into `Drawable` 67 | s_Program = 68 | std::make_unique(PTSD_ASSETS_DIR "/shaders/Base.vert", 69 | PTSD_ASSETS_DIR "/shaders/Base.frag"); 70 | s_Program->Bind(); 71 | 72 | GLint location = glGetUniformLocation(s_Program->GetId(), "surface"); 73 | glUniform1i(location, UNIFORM_SURFACE_LOCATION); 74 | } 75 | 76 | void Text::InitVertexArray() { 77 | s_VertexArray = std::make_unique(); 78 | 79 | // NOLINTBEGIN 80 | // These are vertex data for the rectangle but clang-tidy has magic 81 | // number warnings 82 | 83 | // Vertex 84 | s_VertexArray->AddVertexBuffer(std::make_unique( 85 | std::vector{ 86 | -0.5F, 0.5F, // 87 | -0.5F, -0.5F, // 88 | 0.5F, -0.5F, // 89 | 0.5F, 0.5F, // 90 | }, 91 | 2)); 92 | 93 | // UV 94 | s_VertexArray->AddVertexBuffer(std::make_unique( 95 | std::vector{ 96 | 0.0F, 0.0F, // 97 | 0.0F, 1.0F, // 98 | 1.0F, 1.0F, // 99 | 1.0F, 0.0F, // 100 | }, 101 | 2)); 102 | 103 | // Index 104 | s_VertexArray->SetIndexBuffer( 105 | std::make_unique(std::vector{ 106 | 0, 1, 2, // 107 | 0, 2, 3, // 108 | })); 109 | // NOLINTEND 110 | } 111 | 112 | void Text::ApplyTexture() { 113 | auto surface = 114 | std::unique_ptr>(); 115 | if (m_Font == nullptr) { 116 | surface = {GetMissingFontTextureSDLSurface(), SDL_FreeSurface}; 117 | } else { 118 | surface = 119 | std::unique_ptr>{ 120 | TTF_RenderUTF8_Blended_Wrapped(m_Font.get(), m_Text.c_str(), 121 | m_Color.ToSdlColor(), 0), 122 | SDL_FreeSurface, 123 | }; 124 | } 125 | if (surface == nullptr) { 126 | LOG_ERROR("Failed to create text: {}", TTF_GetError()); 127 | } 128 | 129 | m_Texture->UpdateData(Core::SdlFormatToGlFormat(surface->format->format), 130 | surface->pitch / surface->format->BytesPerPixel, 131 | surface->h, surface->pixels); 132 | m_Size = {surface->pitch / surface->format->BytesPerPixel, surface->h}; 133 | } 134 | 135 | std::unique_ptr Text::s_Program = nullptr; 136 | std::unique_ptr Text::s_VertexArray = nullptr; 137 | 138 | } // namespace Util 139 | -------------------------------------------------------------------------------- /src/Util/Time.cpp: -------------------------------------------------------------------------------- 1 | #include "Util/Time.hpp" 2 | 3 | #include 4 | 5 | namespace Util { 6 | 7 | ms_t Time::GetElapsedTimeMs() { 8 | return static_cast(SDL_GetPerformanceCounter() - s_Start) / 9 | static_cast((SDL_GetPerformanceFrequency())) * 1000.0F; 10 | } 11 | 12 | void Time::Update() { 13 | s_Last = s_Now; 14 | s_Now = SDL_GetPerformanceCounter(); 15 | 16 | s_DeltaTime = static_cast(s_Now - s_Last) / 17 | static_cast(SDL_GetPerformanceFrequency()) * 1000.0F; 18 | } 19 | 20 | sdl_count_t Time::s_Start = SDL_GetPerformanceCounter(); 21 | sdl_count_t Time::s_Now = Time::s_Start; 22 | sdl_count_t Time::s_Last = 0; 23 | ms_t Time::s_DeltaTime = 0; 24 | 25 | } // namespace Util 26 | -------------------------------------------------------------------------------- /src/Util/TransformUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "Util/TransformUtils.hpp" 2 | 3 | #include "config.hpp" 4 | #include 5 | 6 | namespace Util { 7 | Core::Matrices ConvertToUniformBufferData(const Util::Transform &transform, 8 | const glm::vec2 &size, 9 | const float zIndex) { 10 | constexpr glm::mat4 eye(1.F); 11 | 12 | constexpr float nearClip = -100; 13 | constexpr float farClip = 100; 14 | 15 | auto projection = 16 | glm::ortho(0.0F, 1.0F, 0.0F, 1.0F, nearClip, farClip); 17 | auto view = glm::scale(eye, {1.F / PTSD_Config::WINDOW_WIDTH, 18 | 1.F / PTSD_Config::WINDOW_HEIGHT, 1.F}) * 19 | glm::translate(eye, {PTSD_Config::WINDOW_WIDTH / 2, 20 | PTSD_Config::WINDOW_HEIGHT / 2, 0}); 21 | 22 | // TODO: TRS comment 23 | auto model = glm::translate(eye, {transform.translation, zIndex}) * 24 | glm::rotate(eye, transform.rotation, glm::vec3(0, 0, 1)) * 25 | glm::scale(eye, {transform.scale * size, 1}); 26 | 27 | Core::Matrices data = { 28 | model, 29 | projection * view, 30 | }; 31 | 32 | return data; 33 | } 34 | 35 | } // namespace Util 36 | -------------------------------------------------------------------------------- /src/config.cpp: -------------------------------------------------------------------------------- 1 | #include "config.hpp" 2 | #include "SDL_image.h" 3 | 4 | #include 5 | 6 | std::string PTSD_Config::TITLE = "Practice-Tools-for-Simple-Design"; 7 | 8 | int PTSD_Config::WINDOW_POS_X = SDL_WINDOWPOS_UNDEFINED; 9 | int PTSD_Config::WINDOW_POS_Y = SDL_WINDOWPOS_UNDEFINED; 10 | 11 | unsigned int PTSD_Config::WINDOW_WIDTH = 1280; 12 | unsigned int PTSD_Config::WINDOW_HEIGHT = 720; 13 | 14 | Util::Logger::Level PTSD_Config::DEFAULT_LOG_LEVEL = Util::Logger::Level::INFO; 15 | 16 | unsigned int PTSD_Config::FPS_CAP = 60; 17 | 18 | template 19 | inline static void AssignValueFromConfigFile(const nlohmann::json &j, 20 | const std::string_view key, 21 | T &value) { 22 | if (j.contains(key.data())) { 23 | value = j[key.data()].get(); 24 | } 25 | } 26 | 27 | void PTSD_Config::Init() { 28 | nlohmann::json j; 29 | std::ifstream file; 30 | if (std::filesystem::exists("config.json")) { 31 | file = std::ifstream("config.json"); 32 | } else if (std::filesystem::exists("../config.json")) { 33 | file = std::ifstream("../config.json"); 34 | } else { 35 | LOG_WARN("config.json not found, using default configurations."); 36 | return; 37 | } 38 | file >> j; 39 | file.close(); 40 | AssignValueFromConfigFile(j, "title", TITLE); 41 | AssignValueFromConfigFile(j, "window_pos_x", WINDOW_POS_X); 42 | AssignValueFromConfigFile(j, "window_pos_y", WINDOW_POS_Y); 43 | AssignValueFromConfigFile(j, "window_width", WINDOW_WIDTH); 44 | AssignValueFromConfigFile(j, "window_height", WINDOW_HEIGHT); 45 | AssignValueFromConfigFile(j, "default_log_level", DEFAULT_LOG_LEVEL); 46 | AssignValueFromConfigFile(j, "fps_cap", FPS_CAP); 47 | } 48 | -------------------------------------------------------------------------------- /test/Interactive/Audio.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "Util/BGM.hpp" 8 | #include "Util/Logger.hpp" 9 | #include "Util/SFX.hpp" 10 | 11 | bool UserCheck(const std::string &text) { 12 | while (true) { 13 | std::cout << text << "[Y/n] "; 14 | std::string line; 15 | std::getline(std::cin, line); 16 | 17 | if (line[0] == 'Y' || line[0] == 'y' || line.empty()) { 18 | return true; 19 | } 20 | 21 | if (line[0] == 'N' || line[0] == 'n') { 22 | return false; 23 | } 24 | 25 | std::cout << "Invalid input\n"; 26 | } 27 | } 28 | 29 | void EXCEPT_INPUT_YES(const std::string &text) { 30 | if (!UserCheck(text)) { 31 | ADD_FAILURE() << "user test: [" + text + "] is false"; 32 | } 33 | } 34 | 35 | /* 36 | * Or should I just use Core::Context then you will see a window include void 37 | */ 38 | void AudioInit() { 39 | if (Mix_Init(MIX_INIT_MP3) < 0) { 40 | FAIL() << "Failed to initialize SDL_mixer"; 41 | } 42 | 43 | if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) { 44 | FAIL() << "Failed to initialize SDL_mixer"; 45 | } 46 | } 47 | void AudioQuit() { 48 | Mix_HaltGroup(-1); 49 | Mix_CloseAudio(); 50 | 51 | Mix_Quit(); 52 | } 53 | 54 | class AudioTest : public ::testing::Test { 55 | protected: 56 | void SetUp() override { 57 | Util::Logger::Init(); 58 | AudioInit(); 59 | } 60 | 61 | void TearDown() override { AudioQuit(); } 62 | }; 63 | 64 | TEST_F(AudioTest, BGM_TEST) { 65 | auto bgm = Util::BGM("../assets/audio/testbgm.mp3"); 66 | 67 | bgm.Play(); 68 | EXCEPT_INPUT_YES("Do you hear the bgm?"); 69 | 70 | bgm.SetVolume(30); 71 | EXCEPT_INPUT_YES("Is the volume down?"); 72 | 73 | bgm.SetVolume(100); 74 | EXCEPT_INPUT_YES("Is the volume up?"); 75 | 76 | bgm.Pause(); 77 | EXCEPT_INPUT_YES("Is it paused?"); 78 | 79 | bgm.Resume(); 80 | EXCEPT_INPUT_YES("Is it resumed?"); 81 | 82 | bgm.FadeIn(1000); 83 | EXCEPT_INPUT_YES("Is it fading in?"); 84 | 85 | bgm.FadeOut(1000); 86 | EXCEPT_INPUT_YES("Is it fading out?"); 87 | } 88 | 89 | TEST_F(AudioTest, SFX_TEST) { 90 | auto sfx = Util::SFX("../assets/audio/Click.wav"); 91 | 92 | sfx.Play(); 93 | EXCEPT_INPUT_YES("Do you hear the sfx?"); 94 | 95 | sfx.SetVolume(30); 96 | sfx.Play(); 97 | EXCEPT_INPUT_YES("Is the volume lower than last time?"); 98 | 99 | sfx.SetVolume(100); 100 | sfx.Play(); 101 | EXCEPT_INPUT_YES("Is the volume louder than last time?"); 102 | } 103 | 104 | TEST_F(AudioTest, BGM_SFX_TEST) { 105 | auto bgm = Util::BGM("../assets/audio/testbgm.mp3"); 106 | auto sfx = Util::SFX("../assets/audio/Click.wav"); 107 | 108 | bgm.SetVolume(100); 109 | sfx.SetVolume(100); 110 | 111 | bgm.Play(); 112 | EXCEPT_INPUT_YES("Do you hear the bgm?"); 113 | 114 | sfx.Play(); 115 | EXCEPT_INPUT_YES("Do you hear the sfx?"); 116 | 117 | bgm.Pause(); 118 | EXCEPT_INPUT_YES("Is the bgm paused?"); 119 | 120 | sfx.Play(); 121 | EXCEPT_INPUT_YES("Do you hear the sfx?"); 122 | 123 | bgm.Resume(); 124 | EXCEPT_INPUT_YES("Is the bgm resumed?"); 125 | 126 | sfx.Play(); 127 | EXCEPT_INPUT_YES("Do you hear the sfx?"); 128 | 129 | bgm.FadeIn(1000); 130 | EXCEPT_INPUT_YES("Is the bgm fading in?"); 131 | 132 | sfx.Play(); 133 | EXCEPT_INPUT_YES("Do you hear the sfx?"); 134 | 135 | bgm.FadeOut(1000); 136 | EXCEPT_INPUT_YES("Is the bgm fading out?"); 137 | 138 | sfx.Play(); 139 | EXCEPT_INPUT_YES("Do you hear the sfx?"); 140 | } 141 | -------------------------------------------------------------------------------- /test/NotSimpleTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | TEST(NotSimpleTest, TestOnePlusOne) { 4 | ASSERT_EQ(1 + 1, 2); 5 | } 6 | -------------------------------------------------------------------------------- /test/SimpleTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | TEST(SimpleTest, TestTrue) { 4 | ASSERT_TRUE(true); 5 | } 6 | 7 | TEST(SimpleTest, TestFalse) { 8 | ASSERT_FALSE(false); 9 | } 10 | -------------------------------------------------------------------------------- /test/TransformTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Util/Transform.hpp" 4 | 5 | using Util::Transform; 6 | 7 | // Tolerance for trigonometric function results because of PI approximation. 8 | constexpr float M_PI_TOLERANCE = 1e-5F; 9 | 10 | // NOLINTBEGIN(readability-magic-numbers) 11 | 12 | TEST(TransformTest, DefaultConstructor) { 13 | Transform transform = Transform(); 14 | EXPECT_EQ(transform.scale, glm::vec2(1)); 15 | EXPECT_EQ(transform.rotation, 0); 16 | EXPECT_EQ(transform.translation, glm::vec2(0)); 17 | } 18 | 19 | TEST(TransformTest, translation) { 20 | auto expected = glm::vec2(6, 9); 21 | Transform transform; 22 | transform.translation += expected; 23 | 24 | EXPECT_EQ(transform.translation, expected); 25 | } 26 | 27 | TEST(TransformTest, Rotate365) { 28 | auto expected = glm::radians(365.0F); 29 | Transform transform; 30 | transform.rotation += expected; 31 | 32 | auto result = transform.rotation; 33 | EXPECT_NEAR(result, expected, M_PI_TOLERANCE); 34 | } 35 | 36 | TEST(TransformTest, Rotate360) { 37 | auto expected = glm::radians(360.0F); 38 | Transform transform; 39 | transform.rotation += expected; 40 | 41 | auto result = transform.rotation; 42 | EXPECT_NEAR(result, expected, M_PI_TOLERANCE); 43 | } 44 | 45 | TEST(TransformTest, Rotate90) { 46 | auto expected = glm::radians(90.0F); 47 | Transform transform; 48 | transform.rotation += expected; 49 | 50 | auto result = transform.rotation; 51 | EXPECT_NEAR(result, expected, M_PI_TOLERANCE); 52 | } 53 | 54 | TEST(TransformTest, Scaling) { 55 | auto expected = glm::vec2(4, 2); 56 | Transform transform; 57 | transform.scale *= expected; 58 | 59 | auto result = transform.scale; 60 | EXPECT_NEAR(result[0], expected[0], M_PI_TOLERANCE); 61 | EXPECT_NEAR(result[1], expected[1], M_PI_TOLERANCE); 62 | } 63 | 64 | // NOLINTEND(readability-magic-numbers) 65 | --------------------------------------------------------------------------------