Compare commits

..

6 commits

Author SHA1 Message Date
1c8d4abed4
testing...
Some checks failed
continuous-integration/drone/pr Build is failing
2025-07-15 16:23:06 +03:30
93faae4553
testing...
Some checks failed
continuous-integration/drone/pr Build is failing
2025-07-15 16:20:21 +03:30
664a267640
testing...
Some checks failed
continuous-integration/drone/pr Build is failing
2025-07-15 16:19:20 +03:30
f0e09cab86
testing...
Some checks failed
continuous-integration/drone/pr Build is failing
2025-07-15 16:18:51 +03:30
b58e022389
testing...
Some checks reported errors
continuous-integration/drone/pr Build was killed
2025-07-15 16:16:37 +03:30
978291bb59
testing...
Some checks reported errors
continuous-integration/drone/pr Build was killed
2025-07-15 16:15:46 +03:30
263 changed files with 2877 additions and 8461 deletions

View file

@ -1,140 +1,57 @@
---
kind: pipeline
type: exec
name: amd64 — msvc
trigger:
branch:
- main
platform:
os: windows
arch: amd64
steps:
- name: unit tests
shell: powershell
commands:
- ./tools/ci/amd64/msvc/unit_tests.ps1
---
kind: pipeline kind: pipeline
type: docker type: docker
name: amd64 — gcc name: clang format
clone:
recursive: true
submodule_update_remote: true
trigger: trigger:
branch: branch:
- main - main
steps: steps:
- name: unit tests
image: amd64_gcc_unit_tests:latest
pull: if-not-exists
commands:
- ./tools/ci/amd64/gcc/unit_tests.sh
- name: valgrind
image: amd64_gcc_valgrind:latest
pull: if-not-exists
commands:
- ./tools/ci/amd64/gcc/valgrind.sh
---
kind: pipeline
type: docker
name: amd64 — clang
trigger:
branch:
- main
steps:
- name: code coverage
image: amd64_clang_coverage:latest
pull: if-not-exists
environment:
CODECOV_TOKEN:
from_secret: CODECOV_TOKEN
commands:
- ./tools/ci/amd64/clang/coverage.sh
- name: leak sanitizer
image: amd64_clang_lsan:latest
pull: if-not-exists
commands:
- ./tools/ci/amd64/clang/lsan.sh
- name: memory sanitizer
image: amd64_clang_msan:latest
pull: if-not-exists
commands:
- ./tools/ci/amd64/clang/msan.sh
---
kind: pipeline
type: docker
name: static analysis
trigger:
branch:
- main
steps:
- name: clang tidy
image: clang_tidy:latest
pull: if-not-exists
privileged: true
commands:
- ./tools/ci/static_analysis/clang_tidy.sh
- name: clang format - name: clang format
image: clang_format:latest image: clang_format:latest
pull: if-not-exists pull: if-not-exists
commands: commands:
- ./tools/ci/static_analysis/clang_format.sh - |
set -e
clang-format --version
HAS_ISSUES=0
for file in $(find ./modules -name '*.?pp'); do
echo "Checking format for $file"
if ! clang-format --dry-run --Werror "$file"; then
echo "❌ Formatting issue detected in $file"
HAS_ISSUES=1
fi
done
if [ "$HAS_ISSUES" -eq 0 ]; then
echo "✅ All files are properly formatted! Well done! ^~^"
else
echo "❌ Senpai! There was some formatting issues :c"
fi
exit $HAS_ISSUES
--- ---
kind: pipeline kind: pipeline
type: docker type: docker
name: documentation — development name: static analysis
node: clone:
environment: ryali recursive: true
submodule_update_remote: true
trigger: trigger:
branch: branch:
- main - main
steps: steps:
- name: build and deploy - name: static_analysis
image: documentation:latest image: static_analysis:latest
pull: if-not-exists pull: if-not-exists
privileged: true
commands: commands:
- pwd - git submodule update --init --recursive
- cd docs - conan build . -s build_type=Release -o enable_static_analysis=True --build=missing
- mkdir generated
- touch generated/changelogs.rst
- touch generated/api.rst
- sphinx-build -M html . .
- rm -rf /light_docs_dev/*
- mv ./html/* /light_docs_dev/
---
kind: pipeline
type: docker
name: documentation — production
node:
environment: ryali
trigger:
event:
- tag
steps:
- name: build and deploy
image: documentation:latest
pull: if-not-exists
commands:
- pwd
- cd docs
- mkdir generated
- touch generated/changelogs.rst
- touch generated/api.rst
- sphinx-build -M html . .
- rm -rf /light_docs/*
- mv ./html/* /light_docs/

36
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,36 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]: "
labels: ''
assignees: Light3039
---
**Describe the bug**
A clear and concise description of what the bug is.
**Expected behavior**
A clear and concise description of what you expected to happen.
**To Reproduce**
Steps to reproduce the behavior:
1. Make projects "..."
2. Build projects "..."
3. Run project (Mirror/Sandbox) "..."
4. Do "..."
5. See bug "..."
**System (you can omit any of the fields except OS and Console's debug output):**
- OS: [e.g. Manjaro Linux v21.1.0]
- GPU: [e.g. NVIDIA GeForce GTX 1050 Ti Mobile]
- CPU: [e.g. Intel i7-7700HQ (8) @ 3.800GHz]
- Console's debug output (omit if bug occurs before running the program):
**Screenshots**
If applicable, add screenshots to help explain your problem.
If possible add screen shot of console's debug output and neofetch.
**Additional context**
Add any other context about the problem here.

4
.gitmodules vendored
View file

@ -0,0 +1,4 @@
[submodule "external/imgui"]
path = external/imgui
url = https://github.com/ocornut/imgui
branch = docking

View file

@ -1,43 +1,30 @@
cmake_minimum_required(VERSION 3.14) cmake_minimum_required(VERSION 3.14)
project(Light) project(Light)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 23)
set(CMAKE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake)
include(CheckCXXSourceCompiles) include(${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/macros.cmake)
include(${CMAKE_DIR}/functions.cmake)
include(${CMAKE_DIR}/definitions.cmake)
include(${CMAKE_DIR}/dependencies.cmake)
add_option(ENABLE_UNIT_TESTS "Enables the building of the unit test modules") add_option(ENABLE_STATIC_ANALYSIS "Enables clang-tidy static analysis")
add_option(ENABLE_FUZZ_TESTS "Enables the building of the fuzz test modules")
add_option(ENABLE_STATIC_ANALYSIS "Makes clang-tidy checks mandatory for compilation")
if (ENABLE_STATIC_ANALYSIS) if (ENABLE_STATIC_ANALYSIS)
set(CMAKE_CXX_CLANG_TIDY "clang-tidy;--warnings-as-errors=*;--allow-no-checks") set(CMAKE_CXX_CLANG_TIDY "clang-tidy;--warnings-as-errors=*;--allow-no-checks")
endif () endif ()
add_option(ENABLE_LLVM_COVERAGE "Enables the code coverage instrumentation for clang") if(WIN32)
if(ENABLE_LLVM_COVERAGE) add_compile_definitions(LIGHT_PLATFORM_WINDOWS)
if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") elseif(UNIX)
message(FATAL_ERROR "ENABLE_LLVM_COVERAGE only supports the clang compiler") add_compile_definitions(LIGHT_PLATFORM_LINUX)
endif () endif()
# Check for libc++ find_package(glfw3 REQUIRED)
check_cxx_source_compiles(" find_package(glm REQUIRED)
#include <string> find_package(spdlog REQUIRED)
#ifdef _LIBCPP_VERSION find_package(stb REQUIRED)
int main() { return 0; } find_package(yaml-cpp REQUIRED)
#else find_package(EnTT REQUIRED)
#error Not using libc++ find_package(opengl_system REQUIRED)
#endif find_package(nlohmann_json REQUIRED)
" USING_LIBCXX) find_package(lz4 REQUIRED)
if(NOT USING_LIBCXX)
message(FATAL_ERROR "ENABLE_LLVM_COVERAGE requires libc++, please compile with -stdlib=libc++")
endif()
add_compile_options(-fprofile-instr-generate -fcoverage-mapping) add_subdirectory(./modules)
add_link_options(-fprofile-instr-generate -fcoverage-mapping) add_subdirectory(./external)
endif ()
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/modules)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/glad)

View file

@ -1,6 +1,2 @@
# Light # Light
See docs.light7734.com for a comprehensive project documentation See docs.light7734.com for a comprehensive project documentation
<!---FUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUCK
MEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!!!!!!!!-->

View file

@ -11,29 +11,26 @@ class LightRecipe(ConanFile):
generators = "CMakeDeps" generators = "CMakeDeps"
options = { options = {
"enable_unit_tests": [True, False],
"enable_fuzz_tests": [True, False],
"enable_llvm_coverage": [True, False],
"enable_static_analysis": [True, False], "enable_static_analysis": [True, False],
"use_mold": [True, False],
"export_compile_commands": [True, False], "export_compile_commands": [True, False],
} }
default_options = { default_options = {
"enable_unit_tests": True,
"enable_fuzz_tests": False,
"enable_llvm_coverage": False,
"enable_static_analysis": False,
"use_mold": False,
"export_compile_commands": True, "export_compile_commands": True,
"enable_static_analysis": False,
} }
def requirements(self): def requirements(self):
self.requires("imgui/1.92.0-docking") self.requires("gtest/1.16.0")
self.requires("entt/3.15.0") self.requires("entt/3.15.0")
self.requires("glfw/3.4") self.requires("glfw/3.4")
self.requires("glm/1.0.1")
self.requires("spdlog/1.15.3")
self.requires("spirv-cross/1.4.313.0")
self.requires("stb/cci.20240531") self.requires("stb/cci.20240531")
self.requires("volk/1.3.296.0")
self.requires("yaml-cpp/0.8.0") self.requires("yaml-cpp/0.8.0")
self.requires("nlohmann_json/3.12.0")
self.requires("lz4/1.10.0") self.requires("lz4/1.10.0")
def layout(self): def layout(self):
@ -44,13 +41,7 @@ class LightRecipe(ConanFile):
tc.variables["CMAKE_BUILD_TYPE"] = self.settings.build_type tc.variables["CMAKE_BUILD_TYPE"] = self.settings.build_type
if self.options.use_mold:
tc.cache_variables["CMAKE_LINKER_TYPE"] = "MOLD"
tc.cache_variables["CMAKE_EXPORT_COMPILE_COMMANDS"] = self.options.export_compile_commands tc.cache_variables["CMAKE_EXPORT_COMPILE_COMMANDS"] = self.options.export_compile_commands
tc.cache_variables["ENABLE_UNIT_TESTS"] = self.options.enable_unit_tests
tc.cache_variables["ENABLE_FUZZ_TESTS"] = self.options.enable_fuzz_tests
tc.cache_variables["ENABLE_LLVM_COVERAGE"] = self.options.enable_llvm_coverage
tc.cache_variables["ENABLE_STATIC_ANALYSIS"] = self.options.enable_static_analysis tc.cache_variables["ENABLE_STATIC_ANALYSIS"] = self.options.enable_static_analysis
repo = git.Repo(search_parent_directories=True) repo = git.Repo(search_parent_directories=True)

3
docs/.gitignore vendored
View file

@ -1,3 +0,0 @@
_build/
generated/

View file

@ -1,72 +1,62 @@
Asset Management Asset Management
=================================================================================================== ===================================================================================================
Layout
On Disk (file) Layout
--------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------
{version} | 4 bytes, ie. uint32_t
.. code-block:: md {general metadata} | sizeof(AssetMetadata)
{specialized metadata} | sizeof(XXXAssetMetadata), eg. TextureAssetMetadata
{version} | 4 bytes, ie. uint32_t {n} | 4 bytes, ie. uint32_t
{general metadata} | sizeof(AssetMetadata) {blob_0...n metadata} | n * sizeof(BlobMetadata)
{specialized metadata} | sizeof(XXXAssetMetadata), eg. TextureAssetMetadata {blob_0...n data} | variable size based on actual data
{n} | 4 bytes, ie. uint32_t {end marker} | 8 byte, ie size_t for marking the END
{blob_0...n metadata} | n * sizeof(BlobMetadata)
{blob_0...n data} | variable size based on actual data
{end marker} | 8 byte, ie size_t for marking the END
Sections Sections
--------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------
version -> The version of the asset for forward compatibility
.. code-block:: md general metadata -> Common asset metadata such as file-path, asset-type, creator, etc.
specialized metadata -> Metadata specific to the asset, eg. texture dimensions for Textures.
version -> The version of the asset for forward compatibility n -> The number of blobs.
general metadata -> Common asset metadata such as file-path, asset-type, creator, etc. blob_0...n metadata -> Metadata specifying how the actual data is packed, required for unpacking.
specialized metadata -> Metadata specific to the asset, eg. texture dimensions for Textures. blob_0...n data -> The actual data, packed and compressed to be reacdy for direct engine consumption.
n -> The number of blobs.
blob_0...n metadata -> Metadata specifying how the actual data is packed, required for unpacking.
blob_0...n data -> The actual data, packed and compressed to be reacdy for direct engine consumption.
Loading Loading
--------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------
Loading pre-baked asset files (like .png files) for baking: Each `Loader` has ONE OR MORE supported input file types (detected via the file extension): eg. StbLoader -> Can read in .jpg, .png, .bmp, etc.... files
Each `Loader` has ONLY ONE supported output asset type:
Each **Loader** has ONE OR MORE supported input file types (detected via the file extension): eg. StbLoader -> Can read in .jpg, .png, .bmp, etc.... files
Each **Loader** has ONLY ONE supported output asset type:
eg. StbLoader -> outputs TextureAsset eg. StbLoader -> outputs TextureAsset
Multiple **Loader**\s MAY have as output the same asset type: Multiple `Loader`s MAY have as output the same asset type:
eg. StbLoader -> outputs TextureAsset eg. StbLoader -> outputs TextureAsset
eg. SomeOtherImgLoader -> outputs TextureAsset eg. SomeOtherImgLoader -> outputs TextureAsset
Multiple **Loader**\s SHOULD NOT have as input same extension types Multiple `Loader`s SHOULD NOT have as input same extension types
eg. .jpg, .png -> if supported, should only be supported by 1 **Loader** class eg. .jpg, .png -> if supported, should only be supported by 1 `Loader` class
Each **Loader** SHOULD read and turn the data from a file (eg. .png for textures) into something Each `Loader` SHOULD read and turn the data from a file (eg. .png for textures) into something
understandable by a **Packer** (not the engine itself). understandable by a `Packer` (not the engine itself).
A **Loader** SHOULD NOT be responsible for packing the parsed file data into asset data, A `Loader` SHOULD NOT be responsible for packing the parsed file data into asset data,
as that implies direct understanding of the layout required by the engine. as that implies direct understanding of the layout required by the engine.
And if that layout changes, ALL **Loader**s should change accordingly; And if that layout changes, ALL `Loader`s should change accordingly;
which makes a class that's responsible for reading files dependant on the engine's (potentially frequent) internal changes. which makes a class that's responsible for reading files dependant on the engine's (potentially frequent) internal changes.
The logic is to reduce many-to-one dependency into a one-to-one dependency by redirecting the packing process to **Packer** classes The logic is to reduce many-to-one dependency into a one-to-one dependency by redirecting the packing process to `Packer` classes
Packing Packing
--------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------
Each **Packer** is responsible for packing ONLY ONE asset type: Each `Packer` is responsible for packing ONLY ONE asset type:
eg. TexturePacker for packing texture assets from parsed image files. eg. TexturePacker for packing texture assets from parsed image files.
eg. ModelPacker for packing model assets from parsed model files. eg. ModelPacker for packing model assets from parsed model files.
Each **Packer** will output ONE OR MORE blobs of data, Each `Packer` will output ONE OR MORE blobs of data,
and for EACH blob of data, it'll write a BlobMetadata, AFTER the specialized metadata (eg. TextureAssetMetadata) and for EACH blob of data, it'll write a BlobMetadata, AFTER the specialized metadata (eg. TextureAssetMetadata)
A **Packer** will make use of the **Compressor** classes to compress the data, A `Packer` will make use of the `Compressor` classes to compress the data,
and lay it out in a way that is suitable for the engine's consumption. and lay it out in a way that is suitable for the engine's consumption.
Unpacking Unpacking
--------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------
A **Parser** is responsible for parsing ONLY ONE asset type: A `Parser` is responsible for parsing ONLY ONE asset type:
eg. TextureParser for parsing texture assets for direct engine consumption. eg. TextureParser for parsing texture assets for direct engine consumption.
eg. ModelParser for parsing model assets for direct engine consumption. eg. ModelParser for parsing model assets for direct engine consumption.

View file

@ -1,5 +1,4 @@
.. architecture/resources
Resource Management Resource Management
=================================================================================================== ===================================================================================================

View file

@ -1,27 +0,0 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'light'
copyright = '2025, light7734'
author = 'light7734'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = []
templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']

View file

@ -1,68 +0,0 @@
from git import Repo
import re
repo = Repo(search_parent_directories=True)
assert not repo.bare
file_path = "generated/changelog.rst"
messages = []
short_shas = []
hex_shas = []
logs = []
remote_url = "https://git.light7734.com/light7734/light/commit"
def format_log(commit_type, message, major, minor, patch, short_sha, hex_sha):
href = f"{remote_url}/{hex_sha}"
version = f"{major}.{minor}.{patch}-kitten+{short_sha}";
link = f"`{version} <{remote_url}/{hex_sha}>`__"
return f"| **{message}** ({link})"
for commit in repo.iter_commits():
messages.append(commit.summary)
short_shas.append(repo.git.rev_parse(commit.hexsha, short=5))
hex_shas.append(commit.hexsha)
ver_major = 0
ver_minor = 0
ver_patch = 0
idx = len(messages)
for message in reversed(messages):
idx = idx - 1;
commit_type = re.match("^(feat|fix|refactor|perf|build|asset|test|chore|ci|docs)", message)
if not commit_type:
continue
match commit_type.group(0):
case "feat":
ver_minor = ver_minor + 1
ver_patch = 0
case "fix":
ver_patch = ver_patch + 1
case "refactor":
ver_patch = ver_patch + 1
case "perf":
ver_patch = ver_patch + 1
case "build":
ver_patch = ver_patch + 1
case "asset":
ver_patch = ver_patch + 1
logs.append(format_log(commit_type, message, ver_major, ver_minor, ver_patch, short_shas[idx], hex_shas[idx]))
with open(file_path, "w") as f:
f.write(".. changelogs\n\n\n")
f.write("Changelogs\n")
f.write("==================================================\n\n")
f.write("KITTEN\n")
f.write("--------------------------------------------------\n\n")
for log in reversed(logs):
f.write(log + '\n')

View file

@ -1,10 +0,0 @@
.. guidelines/conventions
Coding Conventions
===================================================================================================
Any line of code added to the engine, must abide by following conventions.
They may seem arbitrary, and sometimes they are. But to achieve **consistency**, which is not an arbitrary goal, is to
follow these guidelines.
AAA
--------------------

View file

@ -1,147 +0,0 @@
.. guidelines/development
Development
===================================================================================================
As a solo-project, I am not only the **developer**, but also the **manager**.
Therefore there is a need, if this project is to succeed, to have a development plan.
Such a plan should:
- Define a way to **distribute work** (across time, since there's only 1 developer).
- Define what is a **unit of work** (cycles).
- Provide a way to **track productivity**, which helps projecting the future and **detecting patterns** early on.
- Provide a **pipeline** for the work to go through and **minimize ambiguity**.
These are the **management** aspects of the project, which help the development goals to be more **pragmatic**
---by pulling my mind out of its **engineering dreamland**, and make it focus on the **broader picture**.
Cycle
---------------------------------------------------------------------------------------------------
A cycle is one **step** in development, one cycle = one ticket, and it consists of 4 stages:
1 - Make it known
- Write the commit message.
- This limits the **scope of changes** and gives you a very specific **goal** to work towards.
- If something outside of this scope really bothers you, fix and stash for a future cycle.
- Make a ticket if stash-fix is implausible ---**DO NOT** write **todo** comments.
- The message should follow the project's **commit message specifications**.
- Make a ticket.
- Version control (git) is a **development-tool**, not a **management-tool**.
- Provide a very brief description ---This may be used in the commit message's body.
2 - Make it work
- Write high-level tests that confirms the cycle's requirements are met.
- That is, specify requirements in a programming language instead of English.
- You're done when all the tests pass.
- Preferably write the tests first, but it's okay to start with the interface.
- Tests may not be necessary depending on the requirements and commit type.
- "Make it work" doesn't mean liberally producing shit code, you should:
- Follow project's **conventions**.
- Follow **best practices** and **proven swe principles**.
- Enable **warnings as errors**.
- Enable **static analysis**.
- Don't break any pre-existing-tests.
- Have the over-all picture in mind.
3 - Make it right
- Test driven refactoring
- Now you have a better picture of how things relate and work.
- Switch to a TDD-style development to do the refactoring while following swe best-practices and proven-principles.
4 - Make it fast
- This is an engine, at the end of the day, **performance** is king.
- Get a performance and/or memory profile and try to alleviate the bottlenecks.
- Avoid premature optimizations, be certain what you change has performance benefits.
Sprint
---------------------------------------------------------------------------------------------------
A sprint is the collection of all the finished cycles in one week.
It's meant to provide insight on development speed and help projecting the future.
Commit Message Specification
---------------------------------------------------------------------------------------------------
The project follows the `Conventional Commits Specification <https://www.conventionalcommits.org/en/v1.0.0-beta.4>`_.
.. code-block:: md
<type>[optional scope]: <description>
[optional body]
[optional footer]
With the following commit types:
- feat
- For adding a new feature.
- Causes a **minor** bump in version.
- fix
- For changes that fix one or more bug.
- Causes a **patch** bump in version.
- refactor
- For non feat/fix changes that improve the implementation and/or the interface.
- Causes a **patch** bump in version.
- perf
- For changes that (hopefully) improve the performance.
- Causes a **patch** bump in version.
- build
- For changes that affect the build system or external dependencies.
- Causes a **patch** bump in version.
- asset
- For changes to the files under the ``/data`` directory.
- Causes a **patch** bump in version.
- test
- For adding missing tests or correcting the existing tests.
- Does not affect the version.
- chore
- For releases, .gitignore changes, deleting unused files, etc.
- Does not affect the version.
- ci
- For changes to our CI configuration files and scripts, including files under ``/tools/ci``.
- Does not affect the version.
- docs
- For changes to the documentations.
- Does not affect the version.
Semantic Versioning
---------------------------------------------------------------------------------------------------
Coupled with conventional commit style messages, we can automajically version the project following
the **Semantic Versioning 2.0.0** specifications.
The full version identifier consits of a version core (major.minor.patch) + label + hexsha of the commit.
Using the following format:
.. code-block:: md
<major>.<minor>.<patch>-<label>+<short_hexsha>
eg.
0.8.1-kitten+ea898
0.5.0-kitten+01d85
1.5.0-akasha+7de53
kitten refers to all pre-release (1.0.0) versions
The shortened hexsha of a commit is obtained by:
``git rev-parse --short=5 <commit_hexsha>``

View file

@ -1,7 +0,0 @@
.. guidelines/philosophy
Philosophy
===================================================================================================
| **A theory or attitude that acts as a guiding principle for behaviour.**
| --- Oxford Languages

View file

@ -1,32 +0,0 @@
.. light documentation
.. toctree::
:maxdepth: 2
:caption: Light Engine
light/showcase.rst
light/features.rst
.. toctree::
:maxdepth: 2
:caption: Software Architecture
architecture/assets.rst
architecture/resource.rst
.. toctree::
:maxdepth: 2
:caption: Development Guidelines
guidelines/philosophy.rst
guidelines/development.rst
guidelines/conventions.rst
.. toctree::
:maxdepth: 2
:caption: Generated Docs
generated/api.rst
generated/changelog.rst

View file

@ -1,4 +0,0 @@
.. light/features
Features
===================================================================================================

View file

@ -1,4 +0,0 @@
.. light/demos
Showcase
===================================================================================================

View file

@ -1,35 +0,0 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

58
external/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,58 @@
# GLAD #
add_subdirectory(./glad)
set(MIRROR_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../modules/mirror/)
set(DEPENDENCIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/)
if (CMAKE_COMPILER_IS_GNUCC)
add_compile_options(-w)
endif()
if(MSVC)
add_compile_options(/MP)
add_compile_options(/W0)
endif()
file(GLOB IMGUI_FILES true ABSOLUTE
${CMAKE_CURRENT_SOURCE_DIR}/imgui/imgui.cpp
${CMAKE_CURRENT_SOURCE_DIR}/imgui/imgui_tables.cpp
${CMAKE_CURRENT_SOURCE_DIR}/imgui/imgui_widgets.cpp
${CMAKE_CURRENT_SOURCE_DIR}/imgui/imgui_draw.cpp
${CMAKE_CURRENT_SOURCE_DIR}/imgui/imgui_demo.cpp
)
set(BACKENDS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/imgui/backends/)
file(GLOB IMGUI_BACKEND_FILES true ABSOLUTE
${BACKENDS_DIR}imgui_impl_opengl3.cpp
${BACKENDS_DIR}imgui_impl_glfw.cpp
# ${BACKENDS_DIR}imgui_impl_vulkan.cpp ${BACKENDS_DIR}imgui_impl_vulkan.h
)
if(WIN32)
file(GLOB IMGUI_WINDOWS_BACKEND_FILES true ABSOLUTE ${CMAKE_CURRENT_SOURCE_DIR}/backends/
${BACKENDS_DIR}imgui_impl_dx11.cpp
${BACKENDS_DIR}imgui_impl_win32.cpp
)
list(APPEND IMGUI_BACKEND_FILES ${IMGUI_WINDOWS_BACKEND_FILES})
endif()
add_compile_definitions(IMGUI_IMPL_OPENGL_LOADER_GLAD)
include_directories(${DEPENDENCIES_DIR}GLFW/include)
include_directories(${DEPENDENCIES_DIR}glm/)
add_library(imgui STATIC ${IMGUI_FILES} ${IMGUI_BACKEND_FILES})
target_include_directories(imgui PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/imgui)
target_link_libraries(
imgui
PUBLIC glad
PUBLIC opengl::opengl
PUBLIC glm::glm
PUBLIC glfw
)
# Copy imconfig.h over
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/configurations/imgui/imconfig.h
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/imgui/)

131
external/configurations/imgui/imconfig.h vendored Normal file
View file

@ -0,0 +1,131 @@
//-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
//-----------------------------------------------------------------------------
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
//---- Define assertion handler. Defaults to calling assert().
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
//---- Disable all of Dear ImGui or don't implement standard windows.
// It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp.
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended.
//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger and other debug tools: ShowMetricsWindow() and ShowStackToolWindow() will be empty.
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
//---- Include imgui_user.h at the end of imgui.h as a convenience
//#define IMGUI_INCLUDE_IMGUI_USER_H
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
//#define IMGUI_USE_WCHAR32
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//---- Use stb_printf's faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
// Requires 'stb_sprintf.h' to be available in the include path. Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf.
// #define IMGUI_USE_STB_SPRINTF
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
//#define IMGUI_ENABLE_FREETYPE
//---- Use stb_truetype to build and rasterize the font atlas (default)
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
//#define IMGUI_ENABLE_STB_TRUETYPE
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
#include <glm/glm.hpp>
#define IM_VEC2_CLASS_EXTRA \
bool operator==(glm::vec2 rhs) { return x == rhs.x && y == rhs.y; } \
bool operator!=(glm::vec2 rhs) { return (*this) == rhs; } \
bool operator==(ImVec2 rhs) { return x == rhs.x && y == rhs.y; } \
bool operator!=(ImVec2 rhs) { return (*this) == rhs; }
#define IM_VEC4_CLASS_EXTRA \
bool operator==(glm::vec4 rhs) { return x == rhs.x && y == rhs.y && z == rhs.z && w == rhs.w; } \
bool operator!=(glm::vec4 rhs) { return (*this) == rhs; } \
bool operator==(ImVec4 rhs) { return x == rhs.x && y == rhs.y && z == rhs.z && w == rhs.w; } \
bool operator!=(ImVec4 rhs) { return (*this) == rhs; }
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
//#define ImDrawIdx unsigned int
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
//struct ImDrawList;
//struct ImDrawCmd;
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback
//---- Debug Tools: Macro to break in Debugger
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
//---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(),
// (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.)
// This adds a small runtime cost which is why it is not enabled by default.
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
//---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
/*
namespace ImGui
{
void MyFunction(const char* name, const MyMatrix44& v);
}
*/

1
external/imgui vendored Submodule

@ -0,0 +1 @@
Subproject commit 250333d895b1067533533dcfab137512745b9689

View file

@ -3,22 +3,22 @@ add_subdirectory(./base)
add_subdirectory(./time) add_subdirectory(./time)
add_subdirectory(./logger) add_subdirectory(./logger)
add_subdirectory(./debug) add_subdirectory(./debug)
add_subdirectory(./math)
#
add_subdirectory(./asset_baker) add_subdirectory(./asset_baker)
add_subdirectory(./asset_parser) add_subdirectory(./asset_parser)
# add_subdirectory(./asset_manager) add_subdirectory(./asset_manager)
#
add_subdirectory(./camera) add_subdirectory(./camera)
# add_subdirectory(./input) add_subdirectory(./input)
# add_subdirectory(./ui) add_subdirectory(./ui)
#
add_subdirectory(./surface) add_subdirectory(./window)
# add_subdirectory(./renderer) add_subdirectory(./renderer)
add_subdirectory(./ecs) add_subdirectory(./ecs)
#
add_subdirectory(./app) add_subdirectory(./app)
# apps # apps
add_subdirectory(./mirror) add_subdirectory(./mirror)
add_subdirectory(test) add_subdirectory(test)

View file

@ -1,2 +1,21 @@
add_library_module(app application.cpp) add_library_module(app
target_link_libraries(app PRIVATE lt_debug) application.cpp
layer.cpp
layer_stack.cpp
)
target_link_libraries(app
PUBLIC
renderer
logger
ui
asset_parser
asset_manager
lt_debug
ecs
window
glad
time
opengl::opengl
EnTT::EnTT
)

View file

@ -0,0 +1,77 @@
#pragma once
#include <time/timer.hpp>
namespace lt {
class Renderer;
class Window;
class Event;
class GraphicsContext;
class UserInterface;
class LayerStack;
extern Scope<class Application> create_application();
class Application
{
public:
Application(const Application &) = delete;
Application(Application &&) = delete;
auto operator=(const Application &) -> Application & = delete;
auto operator=(Application &&) -> Application & = delete;
virtual ~Application();
[[nodiscard]] auto sanity_check() const -> bool;
void game_loop();
[[nodiscard]] auto get_window() -> Window &
{
return *m_window;
}
[[nodiscard]] auto get_layer_stack() -> LayerStack &
{
return *m_layer_stack;
}
static void quit();
protected:
Application();
private:
void update_layers();
void render_layers();
void render_user_interface();
void poll_events();
void on_event(const Event &event);
void log_debug_data() const;
Timer m_timer;
Scope<Window> m_window;
Scope<UserInterface> m_user_interface;
Scope<GraphicsContext> m_graphics_context;
Scope<Renderer> m_renderer;
Scope<LayerStack> m_layer_stack;
static Application *s_instance;
};
} // namespace lt

View file

@ -2,21 +2,21 @@
#include <app/application.hpp> #include <app/application.hpp>
auto main(int argc, char *argv[]) -> int32_t int main(int argc, char *argv[]) // NOLINT
try try
{ {
std::ignore = argc; std::ignore = argc;
std::ignore = argv; std::ignore = argv;
auto application = lt::Scope<lt::app::Application> {}; auto application = lt::Scope<lt::Application> {};
application = lt::app::create_application(); application = lt::create_application();
if (!application)
{ lt::ensure(application, "Failed to create application");
throw std::runtime_error { "Failed to create application\n" }; lt::ensure(application->sanity_check(), "Failed to verify the sanity of the application");
}
application->game_loop(); application->game_loop();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
catch (const std::exception &exp) catch (const std::exception &exp)

View file

@ -0,0 +1,116 @@
#pragma once
namespace lt {
class Event;
class MouseMovedEvent;
class ButtonPressedEvent;
class ButtonReleasedEvent;
class WheelScrolledEvent;
class KeyPressedEvent;
class KeyRepeatEvent;
class KeyReleasedEvent;
class SetCharEvent;
class WindowClosedEvent;
class WindowResizedEvent;
class WindowMovedEvent;
class WindowLostFocusEvent;
class WindowGainFocusEvent;
class Layer
{
public:
Layer(std::string name);
virtual ~Layer() = default;
[[nodiscard]] auto get_name() const -> const std::string &
{
return m_layer_name;
}
virtual void on_update(float deltaTime)
{
}
virtual void on_user_interface_update()
{
}
virtual void on_render()
{
}
auto on_event(const Event &event) -> bool;
protected:
std::string m_layer_name;
virtual auto on_mouse_moved(const MouseMovedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_button_pressed(const ButtonPressedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_button_released(const ButtonReleasedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_wheel_scrolled(const WheelScrolledEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_key_pressed(const KeyPressedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_key_repeat(const KeyRepeatEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_key_released(const KeyReleasedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_set_char(const SetCharEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_window_closed(const WindowClosedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_window_resized(const WindowResizedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_window_moved(const WindowMovedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_window_lost_focus(const WindowLostFocusEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_window_gain_focus(const WindowGainFocusEvent & /*event*/) -> bool
{
return false;
}
};
} // namespace lt

View file

@ -0,0 +1,50 @@
#pragma once
namespace lt {
class Layer;
class Event;
class LayerStack
{
public:
template<typename Layer_T, typename... Args>
void emplace_layer(Args &&...args)
{
attach_layer(create_ref<Layer_T>(std::forward<Args>(args)...));
}
void attach_layer(Ref<Layer> layer);
void detach_layer(const Ref<Layer> &layer);
[[nodiscard]] auto is_empty() const -> bool
{
return m_layers.empty();
}
[[nodiscard]] auto begin() -> std::vector<Ref<Layer>>::iterator
{
return m_layers.begin();
}
[[nodiscard]] auto end() -> std::vector<Ref<Layer>>::iterator
{
return m_layers.end();
}
[[nodiscard]] auto rbegin() -> std::vector<Ref<Layer>>::reverse_iterator
{
return m_layers.rbegin();
}
[[nodiscard]] auto rend() -> std::vector<Ref<Layer>>::reverse_iterator
{
return m_layers.rend();
}
private:
std::vector<Ref<Layer>> m_layers;
};
} // namespace lt

View file

@ -1,48 +0,0 @@
#include <app/application.hpp>
#include <app/system.hpp>
namespace lt::app {
void Application::game_loop()
{
while (true)
{
for (auto &system : m_systems)
{
if (system->tick())
{
return;
}
}
for (auto &system : m_systems_to_be_registered)
{
m_systems.emplace_back(system)->on_register();
}
for (auto &system : m_systems_to_be_unregistered)
{
m_systems.erase(
std::remove(m_systems.begin(), m_systems.end(), system),
m_systems.end()
);
}
if (m_systems.empty())
{
return;
}
}
}
void Application::register_system(Ref<app::ISystem> system)
{
m_systems.emplace_back(std::move(system));
}
void Application::unregister_system(Ref<app::ISystem> system)
{
m_systems_to_be_unregistered.emplace_back(std::move(system));
}
} // namespace lt::app

View file

@ -1,44 +0,0 @@
#pragma once
namespace lt::app {
class ISystem;
extern Scope<class Application> create_application();
/** The main application class.
* Think of this like an aggregate of systems, you register systems through this interface.
* Then they'll tick every "application frame".
*/
class Application
{
public:
Application(const Application &) = delete;
Application(Application &&) = delete;
auto operator=(const Application &) -> Application & = delete;
auto operator=(Application &&) -> Application & = delete;
virtual ~Application() = default;
void game_loop();
void register_system(Ref<app::ISystem> system);
void unregister_system(Ref<app::ISystem> system);
protected:
Application() = default;
private:
std::vector<Ref<app::ISystem>> m_systems;
std::vector<Ref<app::ISystem>> m_systems_to_be_unregistered;
std::vector<Ref<app::ISystem>> m_systems_to_be_registered;
};
} // namespace lt::app

View file

@ -1,27 +0,0 @@
#pragma once
namespace lt::app {
class ISystem
{
public:
ISystem() = default;
virtual ~ISystem() = default;
ISystem(ISystem &&) = default;
ISystem(const ISystem &) = delete;
auto operator=(ISystem &&) -> ISystem & = default;
auto operator=(const ISystem &) -> ISystem & = delete;
virtual void on_register() = 0;
virtual void on_unregister() = 0;
virtual auto tick() -> bool = 0;
};
} // namespace lt::app

View file

@ -0,0 +1,203 @@
#include <app/application.hpp>
#include <app/layer.hpp>
#include <app/layer_stack.hpp>
#include <asset_manager/asset_manager.hpp>
#include <debug/assertions.hpp>
#include <input/events/event.hpp>
#include <input/events/keyboard.hpp>
#include <input/events/window.hpp>
#include <input/input.hpp>
#include <ranges>
#include <renderer/blender.hpp>
#include <renderer/graphics_context.hpp>
#include <renderer/render_command.hpp>
#include <renderer/renderer.hpp>
#include <ui/ui.hpp>
#include <window/linux/window.hpp>
namespace lt {
Application *Application::s_instance = nullptr;
Application::Application(): m_window(nullptr)
{
ensure(!s_instance, "Application constructed twice");
s_instance = this;
m_window = Window::create([this](auto &&PH1) { on_event(std::forward<decltype(PH1)>(PH1)); });
// create graphics context
m_graphics_context = GraphicsContext::create(
GraphicsAPI::OpenGL,
(GLFWwindow *)m_window->get_handle()
);
AssetManager::load_shader(
"LT_ENGINE_RESOURCES_TEXTURE_SHADER",
"data/assets/shaders/texture/vs.asset",
"data/assets/shaders/texture/ps.asset"
);
AssetManager::load_shader(
"LT_ENGINE_RESOURCES_TINTED_TEXTURE_SHADER",
"data/assets/shaders/tinted_texture/vs.asset",
"data/assets/shaders/tinted_texture/ps.asset"
);
AssetManager::load_shader(
"LT_ENGINE_RESOURCES_QUAD_SHADER",
"data/assets/shaders/quads/vs.asset",
"data/assets/shaders/quads/ps.asset"
);
m_renderer = Renderer::create(
(GLFWwindow *)m_window->get_handle(),
lt::GraphicsContext::get_shared_context(),
Renderer::CreateInfo {
.quad_renderer_shader = AssetManager::get_shader("LT_ENGINE_RESOURCES_QUAD_SHADER"),
.texture_renderer_shader = AssetManager::get_shader(
"LT_ENGINE_RESOURCES_TEXTURE_SHADER"
),
.tinted_texture_renderer_shader = AssetManager::get_shader(
"LT_ENGINE_RESOURCES_TINTED_"
"TEXTURE_SHADER"
),
}
);
ensure(m_graphics_context, "lWindow::lWindow: failed to create 'GraphicsContext'");
m_user_interface = UserInterface::create(
(GLFWwindow *)m_window->get_handle(),
lt::GraphicsContext::get_shared_context()
);
m_layer_stack = create_scope<LayerStack>();
}
Application::~Application()
{
/** This is required to make forward-declarations possible:
* https://stackoverflow.com/questions/34072862/why-is-error-invalid-application-of-sizeof-to-an-incomplete-type-using-uniqu
*/
}
void Application::game_loop()
{
m_window->set_visibility(true);
while (!m_window->is_closed())
{
update_layers();
render_layers();
render_user_interface();
poll_events();
}
}
void Application::quit()
{
s_instance->m_window->close();
}
void Application::update_layers()
{
for (auto &it : *m_layer_stack)
{
// narrowing double -> float
it->on_update(static_cast<float>(m_timer.elapsed_time().count()));
}
// TODO(Light): each layer should have their own "delta time"
m_timer.reset();
}
void Application::render_layers()
{
m_renderer->begin_frame();
for (auto &it : *m_layer_stack)
{
it->on_render();
}
m_renderer->end_frame();
}
void Application::render_user_interface()
{
m_user_interface->begin();
for (auto &it : *m_layer_stack)
{
it->on_user_interface_update();
}
m_user_interface->end();
}
void Application::poll_events()
{
m_window->poll_events();
}
void Application::on_event(const Event &event)
{
// window
if (event.has_category(WindowEventCategory))
{
m_window->on_event(event);
if (event.get_event_type() == EventType::WindowResized)
{
m_renderer->on_window_resize(dynamic_cast<const WindowResizedEvent &>(event));
}
}
// input
if (event.has_category(InputEventCategory))
{
Input::instance().on_event(event);
if (!Input::instance().is_receiving_game_events())
{
return;
}
}
for (auto &it : std::ranges::reverse_view(*m_layer_stack))
{
if (it->on_event(event))
{
return;
}
}
}
[[nodiscard]] auto Application::sanity_check() const -> bool
{
log_inf("Checking application sanity...");
ensure(s_instance, "Application not constructed!?");
ensure(m_window, "Window is not initialized");
ensure(m_user_interface, "User interface is not initialized");
ensure(m_graphics_context, "Graphics context is not initialized");
ensure(m_renderer, "Renderer is not initialized");
ensure(m_layer_stack, "Layer_stack is not initialized");
ensure(!m_layer_stack->is_empty(), "Layer_stack is empty");
log_inf("Logging application state...");
this->log_debug_data();
m_graphics_context->log_debug_data();
m_user_interface->log_debug_data();
return true;
}
void Application::log_debug_data() const
{
log_inf("Platform::");
log_inf(" Platform name: {}", constants::platform_name);
log_inf(" Platform identifier: {}", std::to_underlying(constants::platform));
log_inf(" CWD: {}", std::filesystem::current_path().generic_string());
}
} // namespace lt

47
modules/app/src/layer.cpp Normal file
View file

@ -0,0 +1,47 @@
#include <app/layer.hpp>
#include <input/events/char.hpp>
#include <input/events/event.hpp>
#include <input/events/keyboard.hpp>
#include <input/events/mouse.hpp>
#include <input/events/window.hpp>
namespace lt {
Layer::Layer(std::string name): m_layer_name(std::move(name))
{
}
auto Layer::on_event(const Event &event) -> bool
{
switch (event.get_event_type())
{
case EventType::MouseMoved: return on_mouse_moved(dynamic_cast<const MouseMovedEvent &>(event));
case EventType::ButtonPressed:
return on_button_pressed(dynamic_cast<const ButtonPressedEvent &>(event));
case EventType::ButtonReleased:
return on_button_released(dynamic_cast<const ButtonReleasedEvent &>(event));
case EventType::WheelScrolled:
return on_wheel_scrolled(dynamic_cast<const WheelScrolledEvent &>(event));
case EventType::KeyPressed: return on_key_pressed(dynamic_cast<const KeyPressedEvent &>(event));
case EventType::KeyRepeated: return on_key_repeat(dynamic_cast<const KeyRepeatEvent &>(event));
case EventType::KeyReleased:
return on_key_released(dynamic_cast<const KeyReleasedEvent &>(event));
case EventType::SetChar: return on_set_char(dynamic_cast<const SetCharEvent &>(event));
case EventType::WindowClosed:
return on_window_closed(dynamic_cast<const WindowClosedEvent &>(event));
case EventType::WindowResized:
return on_window_resized(dynamic_cast<const WindowResizedEvent &>(event));
case EventType::WindowMoved:
return on_window_moved(dynamic_cast<const WindowMovedEvent &>(event));
case EventType::WindowLostFocus:
return on_window_lost_focus(dynamic_cast<const WindowLostFocusEvent &>(event));
case EventType::WindowGainFocus:
return on_window_gain_focus(dynamic_cast<const WindowGainFocusEvent &>(event));
default: ensure(false, "Invalid event: {}", event.get_info_lt_log());
}
}
} // namespace lt

View file

@ -0,0 +1,18 @@
#include <app/layer.hpp>
#include <app/layer_stack.hpp>
namespace lt {
void LayerStack::attach_layer(Ref<Layer> layer)
{
log_trc("Attaching layer [{}]", layer->get_name());
m_layers.emplace_back(std::move(layer));
}
void LayerStack::detach_layer(const Ref<Layer> &layer)
{
log_trc("Detaching layer [{}]", layer->get_name());
m_layers.erase(std::find(m_layers.begin(), m_layers.end(), layer));
}
} // namespace lt

View file

@ -1,5 +1,5 @@
add_executable_module( add_executable_module(
asset_baker entrypoint/baker.cpp asset_baker baker.cpp
) )
target_link_libraries( target_link_libraries(

View file

@ -131,8 +131,7 @@ public:
return "TextLoader"; return "TextLoader";
} }
[[nodiscard]] auto load(const std::filesystem::path &file_path) const [[nodiscard]] auto load(const std::filesystem::path& file_path) const -> Assets::TextAsset::PackageData
-> Assets::TextAsset::PackageData
{ {
auto stream = std::ifstream { file_path, std::ios::binary }; auto stream = std::ifstream { file_path, std::ios::binary };
if (!stream.good()) if (!stream.good())

View file

@ -5,5 +5,5 @@ add_library_module(asset_manager
target_link_libraries( target_link_libraries(
asset_manager asset_manager
PUBLIC asset_parser PUBLIC asset_parser
PRIVATE logger PRIVATE renderer
) )

View file

@ -6,6 +6,7 @@ add_library_module(asset_parser
target_link_libraries( target_link_libraries(
asset_parser asset_parser
PRIVATE LZ4::lz4_static PUBLIC LZ4::lz4_static
PRIVATE logger PUBLIC nlohmann_json::nlohmann_json
PUBLIC logger
) )

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <asset_parser/compressors/compressors.hpp>
#include <asset_parser/parser.hpp> #include <asset_parser/parser.hpp>
#include <compressors/compressors.hpp>
#include <cstdint> #include <cstdint>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <asset_parser/compressors/compressors.hpp>
#include <asset_parser/parser.hpp> #include <asset_parser/parser.hpp>
#include <compressors/compressors.hpp>
#include <cstdint> #include <cstdint>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <asset_parser/compressors/compressors.hpp> #include <compressors/compressors.hpp>
#include <cstdint> #include <cstdint>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>

View file

@ -1,5 +1,6 @@
#include <asset_parser/assets/text.hpp> #include <asset_parser/assets/text.hpp>
#include <lz4.h> #include <lz4.h>
#include <nlohmann/json.hpp>
namespace Assets { namespace Assets {

View file

@ -1,5 +1,6 @@
#include <asset_parser/assets/texture.hpp> #include <asset_parser/assets/texture.hpp>
#include <lz4.h> #include <lz4.h>
#include <nlohmann/json.hpp>
namespace Assets { namespace Assets {

View file

@ -1,2 +1,3 @@
add_library_module(base) add_library_module(base)
target_precompile_headers(base INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/private/pch.hpp)
target_precompile_headers(base INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/src/pch.hpp)

View file

@ -1,3 +1,3 @@
add_library_module(camera camera.cpp scene.cpp) add_library_module(camera camera.cpp scene.cpp)
target_link_libraries(camera PUBLIC math) target_link_libraries(camera PUBLIC glm::glm)

View file

@ -0,0 +1,34 @@
#pragma once
#include <glm/glm.hpp>
namespace lt {
class Camera
{
public:
Camera() = default;
[[nodiscard]] auto get_projection() const -> const glm::mat4 &
{
return m_projection;
}
[[nodiscard]] auto get_background_color() const -> const glm::vec4 &
{
return m_background_color;
}
void set_background_color(const glm::vec4 &color)
{
m_background_color = color;
}
protected:
glm::mat4 m_projection {};
private:
glm::vec4 m_background_color = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f);
};
} // namespace lt

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <camera/scene.hpp> #include <camera/scene.hpp>
#include <glm/glm.hpp>
namespace lt { namespace lt {

View file

@ -1,35 +0,0 @@
#pragma once
#include <math/mat4.hpp>
#include <math/vec4.hpp>
namespace lt {
class Camera
{
public:
Camera() = default;
[[nodiscard]] auto get_projection() const -> const math::mat4 &
{
return m_projection;
}
[[nodiscard]] auto get_background_color() const -> const math::vec4 &
{
return m_background_color;
}
void set_background_color(const math::vec4 &color)
{
m_background_color = color;
}
protected:
math::mat4 m_projection;
private:
math::vec4 m_background_color = math::vec4(1.0f, 0.0f, 0.0f, 1.0f);
};
} // namespace lt

View file

@ -1,12 +1,11 @@
#include <camera/scene.hpp> #include <camera/scene.hpp>
#include <math/algebra.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <math/trig.hpp>
namespace lt { namespace lt {
SceneCamera::SceneCamera() SceneCamera::SceneCamera()
: m_orthographic_specification { .size = 1000.0f, .near_plane = -1.0f, .far_plane = 10000.0f } : m_orthographic_specification { .size = 1000.0f, .near_plane = -1.0f, .far_plane = 10000.0f }
, m_perspective_specification { .vertical_fov = math::radians(45.0f), , m_perspective_specification { .vertical_fov = glm::radians(45.0f),
.near_plane = 0.01f, .near_plane = 0.01f,
.far_plane = 10000.0f } .far_plane = 10000.0f }
, m_aspect_ratio(16.0f / 9.0f) , m_aspect_ratio(16.0f / 9.0f)
@ -65,19 +64,26 @@ void SceneCamera::set_perspective_near_plane(float near_plane)
void SceneCamera::calculate_projection() void SceneCamera::calculate_projection()
{ {
// TODO(Light): implement ortho perspective
if (m_projection_type == ProjectionType::Orthographic) if (m_projection_type == ProjectionType::Orthographic)
{ {
// throw std::runtime_error { "ortho perspective not supported yet" }; m_projection = glm::ortho(
-m_orthographic_specification.size * 0.5f * m_aspect_ratio,
m_orthographic_specification.size * 0.5f * m_aspect_ratio,
-m_orthographic_specification.size * 0.5f,
m_orthographic_specification.size * 0.5f,
m_orthographic_specification.far_plane,
m_orthographic_specification.near_plane
);
}
else // perspective
{
m_projection = glm::perspective(
m_perspective_specification.vertical_fov,
m_aspect_ratio,
m_perspective_specification.near_plane,
m_perspective_specification.far_plane
);
} }
// defaults to perspective for now...
m_projection = math::perspective(
m_perspective_specification.vertical_fov,
m_aspect_ratio,
m_perspective_specification.near_plane,
m_perspective_specification.far_plane
);
} }
} // namespace lt } // namespace lt

View file

@ -1,3 +1,3 @@
add_library_module(lt_debug instrumentor.cpp) add_library_module(lt_debug instrumentor.cpp)
target_link_libraries(lt_debug PUBLIC logger) target_link_libraries(lt_debug PUBLIC logger)
target_precompile_headers(lt_debug PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/private/pch.hpp) target_precompile_headers(lt_debug PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/pch.hpp)

View file

@ -0,0 +1,36 @@
#pragma once
#include <logger/logger.hpp>
namespace lt {
struct FailedAssertion: std::exception
{
FailedAssertion(const char *file, int line)
{
log_crt("Assertion failed in: {} (line {})", file, line);
}
};
template<typename Expression_T, typename... Args>
constexpr void ensure(Expression_T &&expression, std::format_string<Args...> fmt, Args &&...args)
{
if (!static_cast<bool>(expression))
{
Logger::log(LogLvl::critical, fmt, std::forward<Args>(args)...);
throw ::lt::FailedAssertion(__FILE__, __LINE__);
}
}
template<typename Expression_T>
constexpr void ensure(Expression_T &&expression, const char *message)
{
if (!static_cast<bool>(expression))
{
Logger::log(LogLvl::critical, message);
throw ::lt::FailedAssertion(__FILE__, __LINE__);
}
}
} // namespace lt

View file

@ -1,3 +0,0 @@
#pragma once
#include <lt_debug/assertions.hpp>

View file

@ -1,36 +0,0 @@
#pragma once
#include <format>
#include <logger/logger.hpp>
#include <source_location>
namespace lt {
template<typename Expression_T, typename... Args_T>
struct ensure
{
ensure(
Expression_T expression,
std::format_string<Args_T...> fmt,
Args_T &&...args,
const std::source_location &location = std::source_location::current()
)
{
if (!static_cast<bool>(expression))
{
throw std::runtime_error { std::format(
"exception: {}\nlocation: {}:{}",
std::format(fmt, std::forward<Args_T>(args)...),
location.file_name(),
location.line()
) };
}
}
};
template<typename Expression_T, typename... Args_T>
ensure(Expression_T, std::format_string<Args_T...>, Args_T &&...)
-> ensure<Expression_T, Args_T...>;
} // namespace lt

View file

@ -1,5 +1,5 @@
#include <debug/instrumentor.hpp>
#include <logger/logger.hpp> #include <logger/logger.hpp>
#include <lt_debug/instrumentor.hpp>
namespace lt { namespace lt {

View file

@ -0,0 +1,3 @@
#pragma once
#include <debug/assertions.hpp>

View file

@ -1,4 +1,5 @@
add_library_module(ecs entity.cpp scene.cpp uuid.cpp ) add_library_module(ecs entity.cpp scene.cpp uuid.cpp serializer.cpp)
target_link_libraries(ecs target_link_libraries(ecs
PUBLIC logger lt_debug EnTT::EnTT input camera math PUBLIC logger lt_debug EnTT::EnTT renderer input camera
PRIVATE yaml-cpp::yaml-cpp asset_manager
) )

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <math/vec4.hpp> #include <glm/glm.hpp>
#include <utility> #include <utility>
namespace lt { namespace lt {
@ -15,7 +15,7 @@ struct SpriteRendererComponent
SpriteRendererComponent( SpriteRendererComponent(
Ref<Texture> _texture, Ref<Texture> _texture,
const math::vec4 &_tint = math::vec4 { 1.0f, 1.0f, 1.0f, 1.0f } const glm::vec4 &_tint = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)
) )
: texture(std::move(std::move(_texture))) : texture(std::move(std::move(_texture)))
, tint(_tint) , tint(_tint)
@ -29,7 +29,7 @@ struct SpriteRendererComponent
Ref<Texture> texture; Ref<Texture> texture;
math::vec4 tint {}; glm::vec4 tint {};
}; };
} // namespace lt } // namespace lt

View file

@ -0,0 +1,45 @@
#pragma once
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
namespace lt {
struct TransformComponent
{
TransformComponent(const TransformComponent &) = default;
TransformComponent(
const glm::vec3 &_translation = glm::vec3(0.0f, 0.0f, 0.0f),
const glm::vec3 &_scale = glm::vec3(1.0f, 1.0f, 1.0f),
const glm::vec3 &_rotation = glm::vec3(0.0f, 0.0f, 0.0f)
)
: translation(_translation)
, scale(_scale)
, rotation(_rotation)
{
}
[[nodiscard]] auto get_transform() const -> glm::mat4
{
return glm::translate(translation) * glm::rotate(rotation.z, glm::vec3(0.0f, 0.0f, 1.0f))
* glm::scale(scale);
}
operator const glm::mat4() const
{
return get_transform();
}
glm::vec3 translation;
glm::vec3 scale;
glm::vec3 rotation;
};
} // namespace lt

View file

@ -3,7 +3,7 @@
#include <ecs/components/transform.hpp> #include <ecs/components/transform.hpp>
#include <ecs/uuid.hpp> #include <ecs/uuid.hpp>
#include <entt/entt.hpp> #include <entt/entt.hpp>
#include <functional> #include <glm/glm.hpp>
namespace lt { namespace lt {
@ -13,17 +13,11 @@ class Framebuffer;
class Scene class Scene
{ {
public: public:
template<typename... T> void on_create();
auto group()
{
return m_registry.group(entt::get<T...>);
}
template<typename T> void on_update(float deltaTime);
auto view()
{ void on_render(const Ref<Framebuffer> &targetFrameBuffer = nullptr);
return m_registry.view<T>();
}
auto create_entity( auto create_entity(
const std::string &name, const std::string &name,
@ -32,12 +26,6 @@ public:
auto get_entity_by_tag(const std::string &tag) -> Entity; auto get_entity_by_tag(const std::string &tag) -> Entity;
auto get_entt_registry() -> entt::registry &
{
return m_registry;
}
private: private:
friend class Entity; friend class Entity;
@ -54,12 +42,4 @@ private:
) -> Entity; ) -> Entity;
}; };
namespace ecs {
using Registry = Scene;
using Entity = ::lt::Entity;
} // namespace ecs
} // namespace lt } // namespace lt

View file

@ -1,31 +0,0 @@
#include <ecs/components.hpp>
#include <ecs/entity.hpp>
#include <ecs/scene.hpp>
namespace lt {
auto Scene::create_entity(const std::string &name, const TransformComponent &transform) -> Entity
{
return create_entity_with_uuid(name, UUID(), transform);
}
auto Scene::get_entity_by_tag(const std::string &tag) -> Entity
{
return {};
}
auto Scene::create_entity_with_uuid(
const std::string &name,
UUID uuid,
const TransformComponent &transform
) -> Entity
{
auto entity = Entity { m_registry.create(), this };
entity.add_component<TagComponent>(name);
entity.add_component<TransformComponent>(transform);
entity.add_component<UUIDComponent>(uuid);
return entity;
}
} // namespace lt

View file

@ -1,14 +0,0 @@
#include <ecs/uuid.hpp>
namespace lt {
std::mt19937_64 UUID::s_engine = std::mt19937_64(std::random_device()());
std::uniform_int_distribution<uint64_t>
UUID::s_distribution = std::uniform_int_distribution<uint64_t> {};
UUID::UUID(uint64_t uuid /* = -1 */): m_uuid(uuid == -1 ? s_distribution(s_engine) : uuid)
{
}
} // namespace lt

View file

@ -1,43 +0,0 @@
#pragma once
#include <math/mat4.hpp>
#include <math/vec3.hpp>
namespace lt {
struct TransformComponent
{
TransformComponent(const TransformComponent &) = default;
TransformComponent(
const math::vec3 &_translation = math::vec3(0.0f, 0.0f, 0.0f),
const math::vec3 &_scale = math::vec3(1.0f, 1.0f, 1.0f),
const math::vec3 &_rotation = math::vec3(0.0f, 0.0f, 0.0f)
)
: translation(_translation)
, scale(_scale)
, rotation(_rotation)
{
}
[[nodiscard]] auto get_transform() const -> math::mat4
{
return math::translate(translation)
* math::rotate(rotation.z, math::vec3 { 0.0f, 0.0f, 1.0f }) //
* math::scale(scale);
}
operator const math::mat4() const
{
return get_transform();
}
math::vec3 translation;
math::vec3 scale;
math::vec3 rotation;
};
} // namespace lt

111
modules/ecs/src/scene.cpp Normal file
View file

@ -0,0 +1,111 @@
#include <camera/component.hpp>
#include <ecs/components.hpp>
#include <ecs/entity.hpp>
#include <ecs/scene.hpp>
#include <glm/glm.hpp>
#include <renderer/renderer.hpp>
namespace lt {
void Scene::on_create()
{
/* native scripts */
{
m_registry.view<NativeScriptComponent>().each([](NativeScriptComponent &nsc) {
if (nsc.instance == nullptr)
{
nsc.instance = nsc.CreateInstance();
nsc.instance->on_create();
}
});
}
}
void Scene::on_update(float deltaTime)
{
/* native scripts */
{
m_registry.view<NativeScriptComponent>().each([=](NativeScriptComponent &nsc) {
nsc.instance->on_update(deltaTime);
});
}
}
void Scene::on_render(const Ref<Framebuffer> &targetFrameBuffer /* = nullptr */)
{
auto *sceneCamera = (Camera *)nullptr;
auto *sceneCameraTransform = (TransformComponent *)nullptr;
/* scene camera */
{
m_registry.group(entt::get<TransformComponent, CameraComponent>)
.each([&](TransformComponent &transformComp, CameraComponent &cameraComp) {
if (cameraComp.isPrimary)
{
sceneCamera = &cameraComp.camera;
sceneCameraTransform = &transformComp;
}
});
}
/* draw quads */
{
if (sceneCamera)
{
Renderer::begin_scene(sceneCamera, *sceneCameraTransform, targetFrameBuffer);
m_registry.group(entt::get<TransformComponent, SpriteRendererComponent>)
.each([](TransformComponent &transformComp,
SpriteRendererComponent &spriteRendererComp) {
Renderer::draw_quad(
transformComp,
spriteRendererComp.tint,
spriteRendererComp.texture
);
});
Renderer::end_scene();
}
}
}
auto Scene::create_entity(const std::string &name, const TransformComponent &transform) -> Entity
{
return create_entity_with_uuid(name, UUID(), transform);
}
auto Scene::get_entity_by_tag(const std::string &tag) -> Entity
{
// TagComponent tagComp(tag);
// entt::entity entity = entt::to_entity(m_registry, tagComp);
auto entity = Entity {};
m_registry.view<TagComponent>().each([&](TagComponent &tagComp) {
// if (tagComp.tag == tag)
// entity = entity(entt::to_entity(m_registry, tagComp), this);
});
if (entity.is_valid())
{
return entity;
}
ensure(false, "Scene::get_entity_by_tag: failed to find entity by tag: {}", tag);
return {};
}
auto Scene::create_entity_with_uuid(
const std::string &name,
UUID uuid,
const TransformComponent &transform
) -> Entity
{
auto entity = Entity { m_registry.create(), this };
entity.add_component<TagComponent>(name);
entity.add_component<TransformComponent>(transform);
entity.add_component<UUIDComponent>(uuid);
return entity;
}
} // namespace lt

View file

@ -2,16 +2,14 @@
#include <camera/component.hpp> #include <camera/component.hpp>
#include <ecs/components.hpp> #include <ecs/components.hpp>
#include <ecs/serializer.hpp> #include <ecs/serializer.hpp>
#include <math/vec3.hpp>
#include <math/vec4.hpp>
#include <yaml-cpp/yaml.h> #include <yaml-cpp/yaml.h>
namespace YAML { namespace YAML {
template<> template<>
struct convert<lt::math::vec3> struct convert<glm::vec3>
{ {
static auto encode(const lt::math::vec3 &rhs) -> Node static auto encode(const glm::vec3 &rhs) -> Node
{ {
auto node = Node {}; auto node = Node {};
node.push_back(rhs.x); node.push_back(rhs.x);
@ -20,7 +18,7 @@ struct convert<lt::math::vec3>
return node; return node;
} }
static auto decode(const Node &node, lt::math::vec3 &rhs) -> bool static auto decode(const Node &node, glm::vec3 &rhs) -> bool
{ {
if (!node.IsSequence() || node.size() != 3) if (!node.IsSequence() || node.size() != 3)
{ {
@ -35,9 +33,9 @@ struct convert<lt::math::vec3>
}; };
template<> template<>
struct convert<lt::math::vec4> struct convert<glm::vec4>
{ {
static auto encode(const lt::math::vec4 &rhs) -> Node static auto encode(const glm::vec4 &rhs) -> Node
{ {
auto node = Node {}; auto node = Node {};
node.push_back(rhs.x); node.push_back(rhs.x);
@ -47,7 +45,7 @@ struct convert<lt::math::vec4>
return node; return node;
} }
static auto decode(const Node &node, lt::math::vec4 &rhs) -> bool static auto decode(const Node &node, glm::vec4 &rhs) -> bool
{ {
if (!node.IsSequence() || node.size() != 4) if (!node.IsSequence() || node.size() != 4)
{ {
@ -65,14 +63,14 @@ struct convert<lt::math::vec4>
namespace lt { namespace lt {
auto operator<<(YAML::Emitter &out, const math::vec3 &v) -> YAML::Emitter & auto operator<<(YAML::Emitter &out, const glm::vec3 &v) -> YAML::Emitter &
{ {
out << YAML::Flow; out << YAML::Flow;
out << YAML::BeginSeq << v.x << v.y << v.z << YAML::EndSeq; out << YAML::BeginSeq << v.x << v.y << v.z << YAML::EndSeq;
return out; return out;
} }
auto operator<<(YAML::Emitter &out, const math::vec4 &v) -> YAML::Emitter & auto operator<<(YAML::Emitter &out, const glm::vec4 &v) -> YAML::Emitter &
{ {
out << YAML::Flow; out << YAML::Flow;
out << YAML::BeginSeq << v.x << v.y << v.z << v.w << YAML::EndSeq; out << YAML::BeginSeq << v.x << v.y << v.z << v.w << YAML::EndSeq;
@ -158,10 +156,9 @@ auto SceneSerializer::deserialize(const std::string &file_path) -> bool
.get_component<TransformComponent>(); .get_component<TransformComponent>();
entityTransforomComponent.translation = transformComponent["Translation"] entityTransforomComponent.translation = transformComponent["Translation"]
.as<math::vec3>(); .as<glm::vec3>();
entityTransforomComponent.rotation = transformComponent["Rotation"] entityTransforomComponent.rotation = transformComponent["Rotation"].as<glm::vec3>();
.as<math::vec3>(); entityTransforomComponent.scale = transformComponent["Scale"].as<glm::vec3>();
entityTransforomComponent.scale = transformComponent["Scale"].as<math::vec3>();
} }
/* #TEMPORARY SOLUTION# */ /* #TEMPORARY SOLUTION# */
@ -171,7 +168,7 @@ auto SceneSerializer::deserialize(const std::string &file_path) -> bool
auto &entitySpriteRendererComponent = deserializedEntity auto &entitySpriteRendererComponent = deserializedEntity
.add_component<SpriteRendererComponent>(); .add_component<SpriteRendererComponent>();
entitySpriteRendererComponent.tint = spriteRendererComponent["Tint"] entitySpriteRendererComponent.tint = spriteRendererComponent["Tint"]
.as<math::vec4>(); .as<glm::vec4>();
auto texturePath = spriteRendererComponent["Texture"].as<std::string>(); auto texturePath = spriteRendererComponent["Texture"].as<std::string>();
@ -216,7 +213,7 @@ auto SceneSerializer::deserialize(const std::string &file_path) -> bool
); );
entityCameraComponent.camera.set_background_color( entityCameraComponent.camera.set_background_color(
cameraSpecifications["BackgroundColor"].as<math::vec4>() cameraSpecifications["BackgroundColor"].as<glm::vec4>()
); );
entityCameraComponent.isPrimary = cameraComponent["IsPrimary"].as<bool>(); entityCameraComponent.isPrimary = cameraComponent["IsPrimary"].as<bool>();

12
modules/ecs/src/uuid.cpp Normal file
View file

@ -0,0 +1,12 @@
#include <ecs/uuid.hpp>
namespace lt {
auto UUID::s_engine = std::mt19937_64(std::random_device()());
auto UUID::s_distribution = std::uniform_int_distribution<uint64_t> {};
UUID::UUID(uint64_t uuid /* = -1 */): m_uuid(uuid == -1 ? s_distribution(s_engine) : uuid)
{
}
} // namespace lt

View file

@ -1,2 +1,2 @@
add_library_module(input input.cpp) add_library_module(input input.cpp)
target_link_libraries(input PUBLIC surface math imgui::imgui logger) target_link_libraries(input PUBLIC spdlog::spdlog glm::glm imgui logger)

View file

@ -0,0 +1,41 @@
#pragma once
#include <input/events/event.hpp>
#include <sstream>
namespace lt {
class SetCharEvent: public Event
{
public:
SetCharEvent(unsigned int character): m_character(character)
{
}
[[nodiscard]] auto get_character() const -> int
{
return m_character;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "CharSet: " << m_character;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::SetChar;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | KeyboardEventCategory) & category;
}
private:
const unsigned int m_character;
};
} // namespace lt

View file

@ -0,0 +1,56 @@
#pragma once
namespace lt {
enum class EventType : uint8_t
{
None = 0,
// input
MouseMoved,
WheelScrolled,
ButtonPressed,
ButtonReleased,
KeyPressed,
KeyRepeated,
KeyReleased,
SetChar,
// window
WindowMoved,
WindowResized,
WindowClosed,
WindowLostFocus,
WindowGainFocus,
};
enum EventCategory : uint8_t
{
None = 0,
WindowEventCategory = bit(0),
InputEventCategory = bit(1),
KeyboardEventCategory = bit(2),
MouseEventCategory = bit(3),
};
class Event
{
public:
Event() = default;
virtual ~Event() = default;
[[nodiscard]] virtual auto get_event_type() const -> EventType = 0;
[[nodiscard]] virtual auto get_info_lt_log() const -> std::string = 0;
[[nodiscard]] virtual auto has_category(EventCategory category) const -> bool = 0;
friend auto operator<<(std::ostream &os, const Event &e) -> std::ostream &
{
return os << e.get_info_lt_log();
}
};
} // namespace lt

View file

@ -0,0 +1,107 @@
#pragma once
#include <input/events/event.hpp>
#include <sstream>
namespace lt {
class KeyPressedEvent: public Event
{
public:
KeyPressedEvent(int key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> int
{
return m_key;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "KeyPressed: " << m_key;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::KeyPressed;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | KeyboardEventCategory) & category;
}
private:
const int m_key;
};
class KeyRepeatEvent: public Event
{
public:
KeyRepeatEvent(int key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> int
{
return m_key;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "KeyRepeated: " << m_key;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::KeyRepeated;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | KeyboardEventCategory) & category;
}
private:
const int m_key;
};
class KeyReleasedEvent: public Event
{
public:
KeyReleasedEvent(int key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> int
{
return m_key;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "KeyReleased: " << m_key;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::KeyReleased;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | KeyboardEventCategory) & category;
}
private:
const int m_key;
};
} // namespace lt

View file

@ -0,0 +1,151 @@
#pragma once
#include <glm/glm.hpp>
#include <input/events/event.hpp>
#include <sstream>
namespace lt {
class MouseMovedEvent: public Event
{
public:
MouseMovedEvent(float x, float y): m_position(x, y)
{
}
[[nodiscard]] auto get_position() const -> const glm::vec2 &
{
return m_position;
}
[[nodiscard]] auto get_x() const -> float
{
return m_position.x;
}
[[nodiscard]] auto get_y() const -> float
{
return m_position.y;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "MouseMoved: " << m_position.x << ", " << m_position.y;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::MouseMoved;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | MouseEventCategory) & category;
}
private:
const glm::vec2 m_position;
};
class WheelScrolledEvent: public Event
{
public:
WheelScrolledEvent(float offset): m_offset(offset)
{
}
[[nodiscard]] auto get_offset() const -> float
{
return m_offset;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "WheelScrolled: " << m_offset;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::WheelScrolled;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | MouseEventCategory) & category;
}
private:
const float m_offset;
};
class ButtonPressedEvent: public Event
{
public:
ButtonPressedEvent(int button): m_button(button)
{
}
[[nodiscard]] auto get_button() const -> int
{
return m_button;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "ButtonPressed: " << m_button;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::ButtonPressed;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | MouseEventCategory) & category;
}
private:
const int m_button;
};
class ButtonReleasedEvent: public Event
{
public:
ButtonReleasedEvent(int button): m_button(button)
{
}
[[nodiscard]] auto get_button() const -> int
{
return m_button;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "ButtonReleased: " << m_button;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::ButtonReleased;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | MouseEventCategory) & category;
}
private:
const int m_button;
};
} // namespace lt

View file

@ -0,0 +1,133 @@
#pragma once
#include <glm/glm.hpp>
#include <input/events/event.hpp>
#include <sstream>
namespace lt {
class WindowClosedEvent: public Event
{
public:
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
return "WindowClosedEvent";
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::WindowClosed;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(WindowEventCategory) & category;
}
};
class WindowMovedEvent: public Event
{
public:
WindowMovedEvent(int x, int y): m_position(x, y)
{
}
[[nodiscard]] auto get_position() const -> const glm::ivec2 &
{
return m_position;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "WindwoMoved: " << m_position.x << ", " << m_position.y;
return ss.str();
;
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::WindowMoved;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(WindowEventCategory) & category;
}
private:
const glm::ivec2 m_position;
};
class WindowResizedEvent: public Event
{
public:
WindowResizedEvent(unsigned int width, unsigned int height): m_size(width, height)
{
}
[[nodiscard]] auto get_size() const -> const glm::uvec2 &
{
return m_size;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "WindowResized: " << m_size.x << ", " << m_size.y;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::WindowResized;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(WindowEventCategory) & category;
}
private:
const glm::uvec2 m_size;
};
class WindowLostFocusEvent: public Event
{
public:
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
return "WindowLostFocus";
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::WindowLostFocus;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(WindowEventCategory) & category;
}
};
class WindowGainFocusEvent: public Event
{
public:
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
return "WindowGainFocus";
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::WindowGainFocus;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(WindowEventCategory) & category;
}
};
} // namespace lt

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include <math/vec2.hpp> #include <glm/glm.hpp>
namespace lt { namespace lt {
@ -36,7 +36,7 @@ public:
return instance().m_mouse_buttons[code]; return instance().m_mouse_buttons[code];
} }
static auto get_mouse_position(int /*code*/) -> const math::vec2 & static auto get_mouse_position(int /*code*/) -> const glm::vec2 &
{ {
return instance().m_mouse_position; return instance().m_mouse_position;
} }
@ -66,9 +66,9 @@ private:
std::array<bool, 8> m_mouse_buttons {}; std::array<bool, 8> m_mouse_buttons {};
math::vec2 m_mouse_position; glm::vec2 m_mouse_position;
math::vec2 m_mouse_delta; glm::vec2 m_mouse_delta;
float m_mouse_wheel_delta {}; float m_mouse_wheel_delta {};

View file

@ -1,5 +0,0 @@
#include <input/system.hpp>
namespace lt::input {
} // namespace lt::input

View file

@ -1,64 +0,0 @@
#pragma once
#include <surface/system.hpp>
namespace lt::input {
template<class... Ts>
struct overloads: Ts...
{
using Ts::operator()...;
};
/**
*
* @note If this system is attached, it will always consume the input events f rom surface.
* Therefore if you want any input detection mechanism, callbacks should be setup with this
* system and not directly with surface.
*/
class System
{
public:
System(lt::surface::System &surface_system)
{
surface_system.add_event_listener([this](auto &&event) {
return handle_event(std::forward<decltype(event)>(event));
});
};
private:
auto handle_event(const lt::surface::System::Event &event) -> bool
{
const auto visitor = overloads {
[this](const lt::surface::KeyPressedEvent &event) {
m_keys[event.get_key()] = true;
return true;
},
[](const lt::surface::KeyRepeatEvent &) { return false; },
[](const lt::surface::KeyReleasedEvent &) { return false; },
[](const lt::surface::KeySetCharEvent &) { return false; },
[](const lt::surface::MouseMovedEvent &) { return false; },
[](const lt::surface::WheelScrolledEvent &) { return false; },
[](const lt::surface::ButtonPressedEvent &) { return false; },
[](const lt::surface::ButtonReleasedEvent &) { return false; },
[](const auto &) { return false; },
};
return std::visit(visitor, event);
}
void setup_callbacks(GLFWwindow *handle);
std::array<bool, 512> m_keys {};
};
} // namespace lt::input

View file

@ -1,5 +1,10 @@
#include <imgui.h> #include <imgui.h>
#include <input/events/char.hpp>
#include <input/events/event.hpp>
#include <input/events/keyboard.hpp>
#include <input/events/mouse.hpp>
#include <input/input.hpp> #include <input/input.hpp>
#include <input/key_codes.hpp>
#include <logger/logger.hpp> #include <logger/logger.hpp>
namespace lt { namespace lt {
@ -31,8 +36,8 @@ void Input::restart_input_state()
m_keyboad_keys.fill(false); m_keyboad_keys.fill(false);
m_mouse_buttons.fill(false); m_mouse_buttons.fill(false);
m_mouse_position = math::vec2(0.0f); m_mouse_position = glm::vec2(0.0f);
m_mouse_delta = math::vec2(0.0f); m_mouse_delta = glm::vec2(0.0f);
m_mouse_wheel_delta = 0.0f; m_mouse_wheel_delta = 0.0f;
} }

View file

@ -1 +1,2 @@
add_library_module(logger logger.cpp) add_library_module(logger logger.cpp)
target_link_libraries(logger PUBLIC spdlog::spdlog)

View file

@ -1,9 +1,15 @@
#pragma once #pragma once
#include <format> #include <format>
#include <print> #include <memory>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
/** Severity of a log message. */ /** @brief Severity of a log message.
*
* @note Values reflect spdlog::lvl
*/
enum class LogLvl : uint8_t enum class LogLvl : uint8_t
{ {
/** Lowest and most vebose log level, for tracing execution paths and events */ /** Lowest and most vebose log level, for tracing execution paths and events */
@ -28,7 +34,11 @@ enum class LogLvl : uint8_t
off = 6, off = 6,
}; };
/** Simple console logger */ namespace spdlog {
class logger;
}
/** Responsible for logging */
class Logger class Logger
{ {
public: public:
@ -37,19 +47,26 @@ public:
template<typename... Args> template<typename... Args>
void static log(LogLvl lvl, std::format_string<Args...> fmt, Args &&...args) void static log(LogLvl lvl, std::format_string<Args...> fmt, Args &&...args)
{ {
std::ignore = lvl; instance().spd_logger->log(
std::println(fmt, std::forward<Args>(args)...); (spdlog::level::level_enum)lvl,
std::format(fmt, std::forward<Args>(args)...)
);
} }
void static log(LogLvl lvl, const char *message) void static log(LogLvl lvl, const char *message)
{ {
std::ignore = lvl; instance().spd_logger->log((spdlog::level::level_enum)lvl, message);
std::println("{}", message);
} }
private: private:
Logger() = default; Logger();
~Logger();
auto static instance() -> Logger &;
std::shared_ptr<spdlog::logger> spd_logger;
}; };
template<typename... Args> template<typename... Args>

View file

@ -1 +0,0 @@
#include <logger/logger.hpp>

View file

@ -0,0 +1,18 @@
#include <logger/logger.hpp>
Logger::Logger(): spd_logger(spdlog::stdout_color_mt("Logger"))
{
spd_logger->set_pattern("%^%v%$");
spd_logger->set_level(spdlog::level::level_enum::trace);
}
Logger::~Logger()
{
spdlog::drop_all();
}
auto Logger::instance() -> Logger &
{
static auto logger = Logger {};
return logger;
}

View file

@ -1 +0,0 @@
add_library_module(math)

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