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
368 changed files with 49869 additions and 14146 deletions

View file

@ -1,5 +0,0 @@
CompileFlags:
DriverMode: cl
Add:
- /EHsc
- /std:c++latest

View file

@ -1,158 +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
type: docker
name: amd64 — gcc
name: clang format
clone:
recursive: true
submodule_update_remote: true
trigger:
branch:
- main
steps:
- name: unit tests
image: ci:latest
- name: clang format
image: clang_format:latest
pull: if-not-exists
commands:
- ./tools/ci/amd64/gcc/unit_tests.sh
- |
set -e
clang-format --version
HAS_ISSUES=0
- name: valgrind
image: ci:latest
pull: if-not-exists
commands:
- ./tools/ci/amd64/gcc/valgrind.sh
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
type: docker
name: amd64 — clang
trigger:
branch:
- main
steps:
- name: code coverage
image: ci:latest
pull: if-not-exists
environment:
CODECOV_TOKEN:
from_secret: CODECOV_TOKEN
commands:
- ./tools/ci/amd64/clang/coverage.sh
- name: leak sanitizer
image: ci:latest
pull: if-not-exists
commands:
- ./tools/ci/amd64/clang/lsan.sh
- name: memory sanitizer
image: ci:latest
pull: if-not-exists
commands:
- ./tools/ci/amd64/clang/msan.sh
---
kind: pipeline
type: docker
name: static analysis
clone:
recursive: true
submodule_update_remote: true
trigger:
branch:
- main
steps:
- name: clang tidy
image: ci:latest
- name: static_analysis
image: static_analysis:latest
pull: if-not-exists
privileged: true
commands:
- ./tools/ci/static_analysis/clang_tidy.sh
- name: shell check
image: ci:latest
pull: if-not-exists
commands:
- ./tools/ci/static_analysis/shell_check.sh
- name: clang format
image: ci:latest
pull: if-not-exists
commands:
- ./tools/ci/static_analysis/clang_format.sh
- name: cmake format
image: ci:latest
pull: if-not-exists
commands:
- ./tools/ci/static_analysis/cmake_format.sh
- name: shell format
image: ci:latest
pull: if-not-exists
commands:
- ./tools/ci/static_analysis/shell_format.sh
---
kind: pipeline
type: docker
name: documentation — development
node:
environment: ryali
trigger:
branch:
- main
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_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:
- cd docs
- mkdir generated
- touch generated/changelogs.rst
- touch generated/api.rst
- sphinx-build -M html . .
- rm -rf /light_docs/*
- mv ./html/* /light_docs/
- git submodule update --init --recursive
- conan build . -s build_type=Release -o enable_static_analysis=True --build=missing

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,8 +1,30 @@
cmake_minimum_required(VERSION 3.14)
project(Light)
set(CMAKE_CXX_STANDARD 23)
include(${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/functions.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/definitions.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/options.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/macros.cmake)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/modules)
add_option(ENABLE_STATIC_ANALYSIS "Enables clang-tidy static analysis")
if (ENABLE_STATIC_ANALYSIS)
set(CMAKE_CXX_CLANG_TIDY "clang-tidy;--warnings-as-errors=*;--allow-no-checks")
endif ()
if(WIN32)
add_compile_definitions(LIGHT_PLATFORM_WINDOWS)
elseif(UNIX)
add_compile_definitions(LIGHT_PLATFORM_LINUX)
endif()
find_package(glfw3 REQUIRED)
find_package(glm REQUIRED)
find_package(spdlog REQUIRED)
find_package(stb REQUIRED)
find_package(yaml-cpp REQUIRED)
find_package(EnTT REQUIRED)
find_package(opengl_system REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(lz4 REQUIRED)
add_subdirectory(./modules)
add_subdirectory(./external)

128
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
Discord: Light7734#4652.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View file

@ -1,4 +1,2 @@
# Light
See docs.light7734.com for a comprehensive project documentation
###### “No great thing comes into being all at once, any more than a cluster of grapes or a fig. If you tell me, 'I want a fig,' I will answer that it needs time. Let it flower first, then put forth its fruit and then ripen. I say then, if the fig tree's fruit is not brought to perfection suddenly in a single hour, would you expect to gather the fruit of a persons mind so soon and so easily? I tell you, you must not expect it.” —Epictetus, Discourses 1.15.7-8

55
conanfile.py Normal file
View file

@ -0,0 +1,55 @@
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout
import shutil
import os
import git
class LightRecipe(ConanFile):
name = "Light Engine"
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeDeps"
options = {
"enable_static_analysis": [True, False],
"export_compile_commands": [True, False],
}
default_options = {
"export_compile_commands": True,
"enable_static_analysis": False,
}
def requirements(self):
self.requires("gtest/1.16.0")
self.requires("entt/3.15.0")
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("volk/1.3.296.0")
self.requires("yaml-cpp/0.8.0")
self.requires("nlohmann_json/3.12.0")
self.requires("lz4/1.10.0")
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.variables["CMAKE_BUILD_TYPE"] = self.settings.build_type
tc.cache_variables["CMAKE_EXPORT_COMPILE_COMMANDS"] = self.options.export_compile_commands
tc.cache_variables["ENABLE_STATIC_ANALYSIS"] = self.options.enable_static_analysis
repo = git.Repo(search_parent_directories=True)
tc.cache_variables["GIT_HASH"] = repo.head.object.hexsha
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()

Binary file not shown.

View file

@ -0,0 +1,10 @@
#version 440 core
in vec4 vso_FragmentColor;
out vec4 fso_FragmentColor;
void main()
{
fso_FragmentColor = vso_FragmentColor;
}

Binary file not shown.

View file

@ -0,0 +1,17 @@
#version 440 core
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec4 a_Color;
layout(std140, binding = 0) uniform ub_ViewProjection
{
mat4 viewProjection;
};
layout(location = 0) out vec4 vso_FragmentColor;
void main()
{
gl_Position = viewProjection * a_Position;
vso_FragmentColor = a_Color;
}

Binary file not shown.

View file

@ -0,0 +1,12 @@
#version 450 core
in vec2 vso_TexCoord;
uniform sampler2D u_Texture;
out vec4 fso_FragmentColor;
void main()
{
fso_FragmentColor = texture(u_Texture, vso_TexCoord);
}

Binary file not shown.

View file

@ -0,0 +1,19 @@
#version 450 core
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec2 a_TexCoord;
layout(std140, binding = 0) uniform ub_ViewProjection
{
mat4 u_ViewProjection;
};
layout(location = 0) out vec2 vso_TexCoord;
void main()
{
gl_Position = u_ViewProjection * a_Position;
vso_TexCoord = a_TexCoord;
}

Binary file not shown.

View file

@ -0,0 +1,14 @@
#version 450 core
in vec4 vso_Tint;
in vec2 vso_TexCoord;
uniform sampler2D u_Texture;
out vec4 fso_FragmentColor;
void main()
{
fso_FragmentColor = texture(u_Texture, vso_TexCoord) * vso_Tint;
}

Binary file not shown.

View file

@ -0,0 +1,21 @@
#version 450 core
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec4 a_Tint;
layout(location = 2) in vec2 a_TexCoord;
layout(std140, binding = 0) uniform ub_ViewProjection
{
mat4 u_ViewProjection;
};
out vec4 vso_Tint;
out vec2 vso_TexCoord;
void main()
{
gl_Position = u_ViewProjection * a_Position;
vso_Tint = a_Tint;
vso_TexCoord = a_TexCoord;
}

View file

@ -1 +0,0 @@
The quick brown fox jumps over the lazy dog

View file

@ -1,10 +0,0 @@
#version 450 core
layout(location = 0) in vec3 in_frag_color;
layout(location = 0) out vec4 out_frag_color;
void main()
{
out_frag_color = vec4(in_frag_color, 1.0);
}

Binary file not shown.

View file

@ -1,26 +0,0 @@
#version 450 core
layout(push_constant ) uniform pc {
mat4 view_projection;
};
vec3 positions[3] = vec3[](
vec3(0.0, -0.5, 0.5),
vec3(0.5, 0.5, 0.5),
vec3(-0.5, 0.5, 0.5)
);
vec3 colors[3] = vec3[](
vec3(0.0, 0.0, 0.0),
vec3(0.0, 0.0, 0.0),
vec3(0.0, 0.0, 0.0)
);
layout(location = 0) out vec3 out_frag_color;
void main()
{
gl_Position = view_projection * vec4(positions[gl_VertexIndex], 1.0);
out_frag_color = colors[gl_VertexIndex];
}

Binary file not shown.

48
default_gui_layout.ini Normal file
View file

@ -0,0 +1,48 @@
[Window][Dockspace]
Pos=0,0
Size=1595,720
Collapsed=0
[Window][Debug##Default]
ViewportPos=2078,721
ViewportId=0x9F5F46A1
Size=848,1408
Collapsed=0
[Window][Dear ImGui Demo]
Pos=836,24
Size=759,696
Collapsed=0
DockId=0x00000003,1
[Window][Hierarchy]
Pos=0,24
Size=184,696
Collapsed=0
DockId=0x00000001,0
[Window][Properties]
Pos=836,24
Size=759,696
Collapsed=0
DockId=0x00000003,0
[Window][Game]
Pos=186,24
Size=648,696
Collapsed=0
DockId=0x00000002,0
[Window][Content Browser]
ViewportPos=1359,621
ViewportId=0x371352B7
Size=1274,1296
Collapsed=0
[Docking][Data]
DockSpace ID=0x1ED03EE2 Window=0x5B816B74 Pos=516,375 Size=1595,696 Split=X
DockNode ID=0x00000006 Parent=0x1ED03EE2 SizeRef=834,696 Split=X
DockNode ID=0x00000001 Parent=0x00000006 SizeRef=184,696 Selected=0x29EABFBD
DockNode ID=0x00000002 Parent=0x00000006 SizeRef=648,696 CentralNode=1 Selected=0x26816F31
DockNode ID=0x00000003 Parent=0x1ED03EE2 SizeRef=759,696 Selected=0x199AB496

3
docs/.gitignore vendored
View file

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

View file

@ -1,72 +1,62 @@
Asset Management
===================================================================================================
On Disk (file) Layout
Layout
---------------------------------------------------------------------------------------------------
.. code-block:: md
{version} | 4 bytes, ie. uint32_t
{general metadata} | sizeof(AssetMetadata)
{specialized metadata} | sizeof(XXXAssetMetadata), eg. TextureAssetMetadata
{n} | 4 bytes, ie. uint32_t
{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
{version} | 4 bytes, ie. uint32_t
{general metadata} | sizeof(AssetMetadata)
{specialized metadata} | sizeof(XXXAssetMetadata), eg. TextureAssetMetadata
{n} | 4 bytes, ie. uint32_t
{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
---------------------------------------------------------------------------------------------------
.. code-block:: md
version -> The version of the asset for forward compatibility
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.
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.
version -> The version of the asset for forward compatibility
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.
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 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 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 ONLY ONE supported output asset type:
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. SomeOtherImgLoader -> outputs TextureAsset
Multiple **Loader**\s SHOULD NOT have as input same extension types
eg. .jpg, .png -> if supported, should only be supported by 1 **Loader** class
Multiple `Loader`s SHOULD NOT have as input same extension types
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
understandable by a **Packer** (not the engine itself).
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).
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.
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.
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
---------------------------------------------------------------------------------------------------
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. 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)
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.
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. ModelParser for parsing model assets for direct engine consumption.

View file

@ -1,5 +1,4 @@
.. architecture/resources
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

3
external/.clang-tidy vendored Normal file
View file

@ -0,0 +1,3 @@
# Disable all checks in this subdirectory
Checks: '-*'

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);
}
*/

3
external/glad/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,3 @@
add_library(glad ${CMAKE_CURRENT_SOURCE_DIR}/src/gl.c)
target_include_directories(glad PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(glad PUBLIC opengl::opengl)

311
external/glad/include/KHR/khrplatform.h vendored Normal file
View file

@ -0,0 +1,311 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

8319
external/glad/include/glad/gl.h vendored Normal file

File diff suppressed because it is too large Load diff

7841
external/glad/include/glad/vulkan.h vendored Normal file

File diff suppressed because it is too large Load diff

84
external/glad/include/vk_platform.h vendored Normal file
View file

@ -0,0 +1,84 @@
/* */
/* File: vk_platform.h */
/* */
/*
** Copyright 2014-2025 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
*/
#ifndef VK_PLATFORM_H_
#define VK_PLATFORM_H_
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/*
***************************************************************************************************
* Platform-specific directives and type declarations
***************************************************************************************************
*/
/* Platform-specific calling convention macros.
*
* Platforms should define these so that Vulkan clients call Vulkan commands
* with the same calling conventions that the Vulkan implementation expects.
*
* VKAPI_ATTR - Placed before the return type in function declarations.
* Useful for C++11 and GCC/Clang-style function attribute syntax.
* VKAPI_CALL - Placed after the return type in function declarations.
* Useful for MSVC-style calling convention syntax.
* VKAPI_PTR - Placed between the '(' and '*' in function pointer types.
*
* Function declaration: VKAPI_ATTR void VKAPI_CALL vkCommand(void);
* Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void);
*/
#if defined(_WIN32)
/* On Windows, Vulkan commands use the stdcall convention */
#define VKAPI_ATTR
#define VKAPI_CALL __stdcall
#define VKAPI_PTR VKAPI_CALL
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
#error "Vulkan is not supported for the 'armeabi' NDK ABI"
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE)
/* On Android 32-bit ARM targets, Vulkan functions use the "hardfloat" */
/* calling convention, i.e. float parameters are passed in registers. This */
/* is true even if the rest of the application passes floats on the stack, */
/* as it does by default when compiling for the armeabi-v7a NDK ABI. */
#define VKAPI_ATTR __attribute__((pcs("aapcs-vfp")))
#define VKAPI_CALL
#define VKAPI_PTR VKAPI_ATTR
#else
/* On other platforms, use the default calling convention */
#define VKAPI_ATTR
#define VKAPI_CALL
#define VKAPI_PTR
#endif
#if !defined(VK_NO_STDDEF_H)
#include <stddef.h>
#endif /* !defined(VK_NO_STDDEF_H) */
#if !defined(VK_NO_STDINT_H)
#if defined(_MSC_VER) && (_MSC_VER < 1600)
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
#endif /* !defined(VK_NO_STDINT_H) */
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif

17892
external/glad/src/gl.c vendored Normal file

File diff suppressed because it is too large Load diff

5537
external/glad/src/vulkan.c vendored Normal file

File diff suppressed because it is too large Load diff

1
external/imgui vendored Submodule

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

View file

@ -1,26 +1,24 @@
# engine
add_subdirectory(./std)
add_subdirectory(./bitwise)
add_subdirectory(./env)
add_subdirectory(./memory)
add_subdirectory(./base)
add_subdirectory(./time)
add_subdirectory(./logger)
add_subdirectory(./debug)
add_subdirectory(./math)
#
add_subdirectory(./asset_baker)
add_subdirectory(./assets)
#
add_subdirectory(./asset_parser)
add_subdirectory(./asset_manager)
add_subdirectory(./camera)
add_subdirectory(./input)
# add_subdirectory(./ui)
#
add_subdirectory(./surface)
add_subdirectory(./ui)
add_subdirectory(./window)
add_subdirectory(./renderer)
add_subdirectory(./ecs)
#
add_subdirectory(./app)
# apps
# apps
add_subdirectory(./mirror)
add_subdirectory(test)

View file

@ -1,5 +1,21 @@
add_library_module(app application.cpp)
target_link_libraries(
app
PUBLIC memory
PRIVATE lt_debug)
add_library_module(app
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

@ -0,0 +1,27 @@
#pragma once
#include <app/application.hpp>
int main(int argc, char *argv[]) // NOLINT
try
{
std::ignore = argc;
std::ignore = argv;
auto application = lt::Scope<lt::Application> {};
application = lt::create_application();
lt::ensure(application, "Failed to create application");
lt::ensure(application->sanity_check(), "Failed to verify the sanity of the application");
application->game_loop();
return EXIT_SUCCESS;
}
catch (const std::exception &exp)
{
log_crt("Terminating due to uncaught exception:");
log_crt("\texception.what(): {}", exp.what());
return EXIT_FAILURE;
}

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,55 +0,0 @@
#include <app/application.hpp>
#include <app/system.hpp>
#include <memory/reference.hpp>
namespace lt::app {
void Application::game_loop()
{
while (true)
{
for (auto &system : m_systems)
{
const auto &last_tick = system->get_last_tick_result();
const auto now = std::chrono::steady_clock::now();
system->tick(
TickInfo {
.delta_time = now - last_tick.end_time,
.budget = std::chrono::milliseconds { 10 },
.start_time = now,
}
);
}
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(memory::Ref<app::ISystem> system)
{
m_systems.emplace_back(std::move(system));
}
void Application::unregister_system(memory::Ref<app::ISystem> system)
{
m_systems_to_be_unregistered.emplace_back(std::move(system));
}
} // namespace lt::app

View file

@ -1,47 +0,0 @@
#pragma once
#include <memory/reference.hpp>
#include <memory/scope.hpp>
namespace lt::app {
class ISystem;
extern memory::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(memory::Ref<app::ISystem> system);
void unregister_system(memory::Ref<app::ISystem> system);
protected:
Application() = default;
private:
std::vector<memory::Ref<app::ISystem>> m_systems;
std::vector<memory::Ref<app::ISystem>> m_systems_to_be_unregistered;
std::vector<memory::Ref<app::ISystem>> m_systems_to_be_registered;
};
} // namespace lt::app

View file

@ -1,28 +0,0 @@
#pragma once
#include <app/application.hpp>
#include <logger/logger.hpp>
#include <memory/scope.hpp>
auto main(int argc, char *argv[]) -> int32_t
try
{
std::ignore = argc;
std::ignore = argv;
auto application = lt::memory::Scope<lt::app::Application> {};
application = lt::app::create_application();
if (!application)
{
throw std::runtime_error { "Failed to create application\n" };
}
application->game_loop();
return EXIT_SUCCESS;
}
catch (const std::exception &exp)
{
lt::log::critical("Terminating due to uncaught exception:");
lt::log::critical("\texception.what(): {}", exp.what());
return EXIT_FAILURE;
}

View file

@ -1,112 +0,0 @@
#pragma once
#include <chrono>
#include <logger/logger.hpp>
namespace lt::app {
/** Information required to tick a system.
* @note May be used across an entire application-frame (consisting of multiple systems ticking)
*/
struct TickInfo
{
using Timepoint_T = std::chrono::time_point<std::chrono::steady_clock>;
using Duration_T = std::chrono::duration<double>;
/** Duration since previous tick's end_time to current tick's start_time. */
Duration_T delta_time {};
/** Maximum duration the system is expected to finish ticking in.
*
* if end_time - start_time > budget -> the system exceeded its ticking budget.
* else end_time - start_time < budget -> the system ticked properly.
*
* In other words, end_time is expected to be less than start_time + budget.
*/
Duration_T budget {};
/** Exact time which ticking started. */
Timepoint_T start_time;
};
/** Information about how a system's tick performed */
struct TickResult
{
using Timepoint_T = std::chrono::time_point<std::chrono::steady_clock>;
using Duration_T = std::chrono::duration<double>;
/** The info supplied to the system for ticking. */
TickInfo info;
/** Equivalent to end_time - info.start_time. */
Duration_T duration {};
/** Exact time which ticking ended. */
Timepoint_T end_time;
};
struct SystemDiagnosis
{
enum class Severity : uint8_t
{
verbose,
info,
warning,
error,
fatal,
};
std::string message;
std::string code;
Severity severity;
};
class SystemStats
{
public:
void push_diagnosis(SystemDiagnosis &&diagnosis)
{
auto diag = m_diagnosis.emplace_back(std::move(diagnosis));
log::debug("message: {}", diag.message);
}
[[nodiscard]] auto empty_diagnosis() const -> bool
{
return m_diagnosis.empty();
}
private:
std::vector<SystemDiagnosis> m_diagnosis;
};
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 void tick(TickInfo tick) = 0;
[[nodiscard]] virtual auto get_last_tick_result() const -> const TickResult & = 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,6 +1,10 @@
add_library_module(libasset_baker bakers.cpp)
target_link_libraries(libasset_baker PUBLIC assets logger lt_debug tbb)
add_test_module(libasset_baker bakers.test.cpp)
add_executable_module(
asset_baker baker.cpp
)
add_executable_module(asset_baker entrypoint/baker.cpp)
target_link_libraries(asset_baker PRIVATE libasset_baker)
target_link_libraries(
asset_baker
PRIVATE asset_parser
PRIVATE stb::stb
PRIVATE logger
)

View file

@ -0,0 +1,183 @@
#pragma once
#include <asset_parser/assets/text.hpp>
#include <asset_parser/assets/texture.hpp>
#include <filesystem>
#include <logger/logger.hpp>
#include <string_view>
#include <unordered_set>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
namespace lt {
class Loader
{
public:
[[nodiscard]] virtual auto get_name() const -> std::string_view = 0;
Loader() = default;
Loader(Loader &&) = default;
Loader(const Loader &) = delete;
auto operator=(Loader &&) -> Loader & = default;
auto operator=(const Loader &) -> Loader & = delete;
virtual ~Loader() = default;
private:
};
class TextureLoader: public Loader
{
public:
TextureLoader() = default;
[[nodiscard]] virtual auto load(std::filesystem::path file_path) const
-> Assets::TextureAsset::PackageData
= 0;
};
class StbLoader: public TextureLoader
{
public:
StbLoader() = default;
void load(std::filesystem::path path);
[[nodiscard]] static auto get_supported_extensions() -> std::unordered_set<std::string_view>
{
return { ".png" };
}
[[nodiscard]] auto get_name() const -> std::string_view override
{
return "StbLoader";
}
[[nodiscard]] auto load(std::filesystem::path file_path) const
-> Assets::TextureAsset::PackageData override
{
auto width = int {};
auto height = int {};
auto channels = int {};
auto *pixels = stbi_load(file_path.string().c_str(), &width, &height, &channels, 4);
if (!pixels)
{
throw std::runtime_error {
std::format("Failed to load image file at: {} using stbi_load", file_path.string()),
};
}
const auto metadata = Assets::Asset::Metadata {
.type = Assets::Asset::Type::Texture,
};
const auto texture_metadata = Assets::TextureAsset::Metadata {
.format = Assets::TextureAsset::Format::RGBA8,
.num_components = static_cast<uint32_t>(channels),
.pixel_size = {
static_cast<uint32_t>(width),
static_cast<uint32_t>(height),
{},
},
};
auto pixels_blob = Assets::Blob {};
pixels_blob.resize(static_cast<size_t>(width) * height * channels);
// TODO(Light): figure out if it's possible to directly populate a blob with stbi functions
memcpy(pixels_blob.data(), pixels, pixels_blob.size());
stbi_image_free(pixels);
return Assets::TextureAsset::PackageData {
.metadata = metadata,
.texture_metadata = texture_metadata,
.pixels = std::move(pixels_blob),
};
}
};
class TextureLoaderFactory
{
public:
static auto create(std::string_view file_extension) -> std::unique_ptr<TextureLoader>
{
if (StbLoader::get_supported_extensions().contains(file_extension))
{
return std::make_unique<StbLoader>();
}
return {};
}
};
class TextLoader: Loader
{
public:
[[nodiscard]] static auto get_supported_extensions() -> std::unordered_set<std::string_view>
{
return { ".glsl", ".txt", ".hlsl" };
}
[[nodiscard]] auto get_name() const -> std::string_view override
{
return "TextLoader";
}
[[nodiscard]] auto load(const std::filesystem::path& file_path) const -> Assets::TextAsset::PackageData
{
auto stream = std::ifstream { file_path, std::ios::binary };
if (!stream.good())
{
throw std::runtime_error {
std::format(
"Failed to open ifstream for text loading of file: {}",
file_path.string()
),
};
}
auto file_size = std::filesystem::file_size(file_path);
auto text_blob = Assets::Blob(file_size);
stream.read((char *)(text_blob.data()), static_cast<long>(file_size)); // NOLINT
const auto metadata = Assets::Asset::Metadata {
.type = Assets::Asset::Type::Text,
};
const auto text_metadata = Assets::TextAsset::Metadata {
.lines = {},
};
return Assets::TextAsset::PackageData {
.metadata = metadata,
.text_metadata = {},
.text_blob = std::move(text_blob),
};
}
};
class TextLoaderFactory
{
public:
static auto create(std::string_view file_extension) -> std::unique_ptr<TextLoader>
{
if (TextLoader::get_supported_extensions().contains(file_extension))
{
return std::make_unique<TextLoader>();
}
return {};
}
};
} // namespace lt

View file

@ -1,2 +0,0 @@
#include <asset_baker/bakers.hpp>
#include <test/test.hpp>

View file

@ -1,42 +0,0 @@
#include <asset_baker/bakers.hpp>
#include <assets/shader.hpp>
#include <logger/logger.hpp>
auto main(int argc, char *argv[]) -> int32_t
try
{
if (argc != 2)
{
throw std::logic_error("Argc should be 2 -- exe dir (implicit) and target dir");
}
for (const auto &directory_iterator :
std::filesystem::recursive_directory_iterator(argv[1])) // NOLINT
{
if (directory_iterator.is_directory())
{
continue;
}
const auto &in_path = directory_iterator.path();
const auto out_path = std::format("{}.asset", in_path.c_str());
if (in_path.extension() == ".vert")
{
bake_shader(in_path, out_path, lt::assets::ShaderAsset::Type::vertex);
}
else if (in_path.extension() == ".frag")
{
bake_shader(in_path, out_path, lt::assets::ShaderAsset::Type::fragment);
}
}
return EXIT_SUCCESS;
}
catch (const std::exception &exp)
{
lt::log::critical("Terminating due to uncaught exception:");
lt::log::critical("\texception.what: {}:", exp.what());
return EXIT_FAILURE;
}

View file

@ -1,65 +0,0 @@
#pragma once
#include <assets/shader.hpp>
#include <logger/logger.hpp>
inline void bake_shader(
const std::filesystem::path &in_path,
const std::filesystem::path &out_path,
lt::assets::ShaderAsset::Type type
)
{
using lt::assets::ShaderAsset;
using enum lt::assets::ShaderAsset::Type;
auto glsl_path = in_path.string();
auto spv_path = std::format("{}.spv", glsl_path);
lt::log::trace(
"Compiling {} shader {} -> {}",
type == vertex ? "vertex" : "fragment",
glsl_path,
spv_path
);
// Don't bother linking to shaderc, just invoke the command with a system call.
// NOLINTNEXTLINE(concurrency-mt-unsafe)
system(
std::format(
"glslc --target-env=vulkan1.4 -std=450core -fshader-stage={} {} -o {}",
type == vertex ? "vert" : "frag",
glsl_path,
spv_path
)
.c_str()
);
auto stream = std::ifstream(spv_path, std::ios::binary);
lt::ensure(
stream.is_open(),
"Failed to open compiled {} shader at: {}",
type == vertex ? "vert" : "frag",
spv_path
);
stream.seekg(0, std::ios::end);
const auto size = stream.tellg();
auto bytes = std::vector<std::byte>(size);
stream.seekg(0, std::ios::beg);
stream.read((char *)bytes.data(), size); // NOLINT
lt::log::debug("BYTES: {}", bytes.size());
stream.close();
std::filesystem::remove(spv_path);
ShaderAsset::pack(
out_path,
lt::assets::AssetMetadata {
.version = lt::assets::current_version,
.type = ShaderAsset::asset_type_identifier,
},
ShaderAsset::Metadata {
.type = type,
},
std::move(bytes)
);
}

View file

@ -0,0 +1,100 @@
#include <asset_baker/bakers.hpp>
#include <asset_parser/assets/text.hpp>
#include <asset_parser/assets/texture.hpp>
#include <asset_parser/parser.hpp>
#include <filesystem>
#include <logger/logger.hpp>
void try_packing_texture(
const std::filesystem::path &in_path,
const std::filesystem::path &out_path
)
{
auto texture_loader = lt::TextureLoaderFactory::create(in_path.extension().string());
if (!texture_loader)
{
// Don't log anything; this is expected.
return;
}
try
{
Assets::TextureAsset::pack(texture_loader->load(in_path), out_path);
log_inf("Packed a texture asset:");
log_inf("\tloader : {}", texture_loader->get_name());
log_inf("\tin path: {}", in_path.string());
log_inf("\tout path: {}", out_path.string());
}
catch (const std::exception &exp)
{
log_err("Failed to pack texture asset:");
log_err("\tloader : {}", texture_loader->get_name());
log_err("\tin path : {}", in_path.string());
log_err("\tout path: {}", out_path.string());
log_err("\texp.what: {}", exp.what());
}
}
void try_packing_text(const std::filesystem::path &in_path, const std::filesystem::path &out_path)
{
auto text_loader = lt::TextLoaderFactory::create(in_path.extension().string());
if (!text_loader)
{
// Don't log anything; this is expected.
return;
}
try
{
Assets::TextAsset::pack(text_loader->load(in_path), out_path);
log_inf("Packed a text asset:");
log_inf("\tloader : {}", text_loader->get_name());
log_inf("\tin path: {}", in_path.string());
log_inf("\tout path: {}", out_path.string());
}
catch (const std::exception &exp)
{
log_err("Failed to pack a text asset:");
log_err("\tloader : {}", text_loader->get_name());
log_err("\tin path : {}", in_path.string());
log_err("\tout path: {}", out_path.string());
log_err("\texp.what: {}", exp.what());
}
}
auto main(int argc, char *argv[]) -> int32_t
try
{
if (argc != 2)
{
throw std::logic_error("Argc should be 2 -- exe dir (implicit) and target dir");
}
for (const auto &directory_iterator :
std::filesystem::recursive_directory_iterator(argv[1])) // NOLINT
{
if (directory_iterator.is_directory())
{
continue;
}
const auto &in_path = directory_iterator.path();
auto out_path = in_path;
out_path.replace_extension(".asset");
try_packing_texture(in_path, out_path);
try_packing_text(in_path, out_path);
}
return EXIT_SUCCESS;
}
catch (const std::exception &exp)
{
log_crt("Terminating due to uncaught exception:");
log_crt("\texception.what: {}:", exp.what());
return EXIT_FAILURE;
}

View file

@ -0,0 +1,9 @@
add_library_module(asset_manager
asset_manager.cpp
)
target_link_libraries(
asset_manager
PUBLIC asset_parser
PRIVATE renderer
)

View file

@ -0,0 +1,78 @@
#pragma once
#include <filesystem>
namespace Assets {
class TextAsset;
class TextureAsset;
} // namespace Assets
namespace lt {
class Shader;
class Texture;
/**
* Asset is the data on the disk.
* Resource is the data on the gpu/cpu
*
* eg. TextureAsset is the file on the disk
* eg. Texture is the representation of it in the GPU
*/
class AssetManager
{
public:
static void load_shader(
const std::string &name,
const std::filesystem::path &vertex_path,
const std::filesystem::path &pixel_path
)
{
instance().load_shader_impl(name, vertex_path, pixel_path);
}
static void load_texture(const std::string &name, const std::filesystem::path &path)
{
instance().load_texture_impl(name, path);
}
static auto get_shader(const std::string &name) -> Ref<Shader>
{
return instance().m_shaders[name];
}
static auto get_texture(const std::string &name) -> Ref<Texture>
{
return instance().m_textures[name];
}
private:
AssetManager() = default;
static auto instance() -> AssetManager &;
void load_shader_impl(
const std::string &name,
const std::filesystem::path &vertex_path,
const std::filesystem::path &pixel_path
);
void load_texture_impl(const std::string &name, const std::filesystem::path &path);
auto get_or_load_text_asset(const std::filesystem::path &path) -> Ref<Assets::TextAsset>;
auto get_or_load_texture_asset(const std::filesystem::path &path) -> Ref<Assets::TextureAsset>;
std::unordered_map<std::string, Ref<Assets::TextAsset>> m_text_assets;
std::unordered_map<std::string, Ref<Assets::TextureAsset>> m_texture_assets;
std::unordered_map<std::string, Ref<Shader>> m_shaders;
std::unordered_map<std::string, Ref<Texture>> m_textures;
};
} // namespace lt

View file

@ -0,0 +1,92 @@
#include <asset_manager/asset_manager.hpp>
#include <asset_parser/assets/text.hpp>
#include <asset_parser/assets/texture.hpp>
#include <logger/logger.hpp>
#include <renderer/graphics_context.hpp>
#include <renderer/shader.hpp>
#include <renderer/texture.hpp>
namespace lt {
/* static */ auto AssetManager::instance() -> AssetManager &
{
static auto instance = AssetManager {};
return instance;
}
void AssetManager::load_shader_impl(
const std::string &name,
const std::filesystem::path &vertex_path,
const std::filesystem::path &pixel_path
)
{
try
{
log_trc("Loading shader:");
log_trc("\tname : {}", name);
log_trc("\tvertex path: {}", vertex_path.string());
log_trc("\tpixel path : {}", pixel_path.string());
m_shaders[name] = Ref<Shader>(Shader::create(
get_or_load_text_asset(vertex_path.string()),
get_or_load_text_asset(pixel_path),
GraphicsContext::get_shared_context()
));
}
catch (const std::exception &exp)
{
log_err("Failed to load shader:");
log_err("\tname : {}", name);
log_err("\tvertex path: {}", vertex_path.string());
log_err("\tpixel path : {}", pixel_path.string());
log_err("\texception : {}", exp.what());
}
}
void AssetManager::load_texture_impl(const std::string &name, const std::filesystem::path &path)
{
try
{
log_trc("Loading texture:");
log_trc("\tname: {}", name);
log_trc("\tpath: {}", path.string());
m_textures[name] = Ref<Texture>(
Texture::create(get_or_load_texture_asset(path), GraphicsContext::get_shared_context())
);
}
catch (const std::exception &exp)
{
log_err("Failed to load texture:");
log_err("\tname : {}", name);
log_err("\tpath : {}", path.string());
log_err("\texception: {}", exp.what());
}
}
auto AssetManager::get_or_load_text_asset(const std::filesystem::path &path)
-> Ref<Assets::TextAsset>
{
const auto key = std::filesystem::canonical(path).string();
if (!m_text_assets.contains(key))
{
m_text_assets.emplace(key, create_ref<Assets::TextAsset>(path));
}
return m_text_assets[key];
}
auto AssetManager::get_or_load_texture_asset(const std::filesystem::path &path)
-> Ref<Assets::TextureAsset>
{
const auto key = std::filesystem::canonical(path).string();
if (!m_texture_assets.contains(key))
{
m_texture_assets.emplace(key, create_ref<Assets::TextureAsset>(path));
}
return m_texture_assets[key];
}
} // namespace lt

View file

@ -0,0 +1,12 @@
add_library_module(asset_parser
parser.cpp
assets/texture.cpp
assets/text.cpp
)
target_link_libraries(
asset_parser
PUBLIC LZ4::lz4_static
PUBLIC nlohmann_json::nlohmann_json
PUBLIC logger
)

View file

@ -0,0 +1,58 @@
#pragma once
#include <asset_parser/parser.hpp>
#include <compressors/compressors.hpp>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <logger/logger.hpp>
namespace Assets {
class TextAsset: public Asset
{
public:
struct Metadata
{
uint32_t lines {};
};
/** Data required to pack a text asset */
struct PackageData
{
Asset::Metadata metadata;
Metadata text_metadata;
Blob text_blob;
};
static void pack(const PackageData &data, const std::filesystem::path &out_path);
TextAsset(const std::filesystem::path &path);
void unpack_blob(
BlobMetadata::Tag tag,
std::byte *destination,
size_t destination_capacity
) const;
[[nodiscard]] auto get_asset_metadata() const -> const Asset::Metadata &;
[[nodiscard]] auto get_metadata() const -> const Metadata &;
[[nodiscard]] auto get_blob_metadata(BlobMetadata::Tag tag) const -> const BlobMetadata &;
private:
uint32_t version {};
Asset::Metadata m_asset_metadata {};
Metadata m_metadata {};
BlobMetadata m_text_blob_metadata {};
mutable std::ifstream m_stream;
};
} // namespace Assets

View file

@ -0,0 +1,64 @@
#pragma once
#include <asset_parser/parser.hpp>
#include <compressors/compressors.hpp>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <logger/logger.hpp>
namespace Assets {
class TextureAsset: public Asset
{
public:
enum class Format : uint32_t // NOLINT(performance-enum-size)
{
None = 0,
RGBA8,
};
struct Metadata
{
Format format;
uint32_t num_components;
std::array<uint32_t, 3> pixel_size;
};
/** Data required to pack a texture asset */
struct PackageData
{
Asset::Metadata metadata;
Metadata texture_metadata;
Blob pixels;
};
static void pack(const PackageData &data, const std::filesystem::path &out_path);
TextureAsset(const std::filesystem::path &path);
void unpack_blob(BlobMetadata::Tag tag, std::byte *destination, size_t destination_capacity);
[[nodiscard]] auto get_asset_metadata() const -> const Asset::Metadata &;
[[nodiscard]] auto get_metadata() const -> const Metadata &;
[[nodiscard]] auto get_blob_metadata(BlobMetadata::Tag tag) const -> const BlobMetadata &;
private:
uint32_t version {};
Asset::Metadata m_asset_metadata {};
Metadata m_metadata {};
BlobMetadata m_pixel_blob_metadata {};
std::ifstream m_stream;
};
} // namespace Assets

View file

@ -0,0 +1,68 @@
#pragma once
#include <compressors/compressors.hpp>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <logger/logger.hpp>
#include <utility>
#include <vector>
namespace Assets {
constexpr auto current_version = uint32_t { 1 };
struct BlobMetadata
{
enum class Tag : uint8_t
{
text,
color,
depth,
vertices,
indices,
};
Tag tag;
size_t offset;
CompressionType compression_type;
size_t compressed_size;
size_t uncompressed_size;
};
using Blob = std::vector<std::byte>;
class Asset
{
public:
enum class Type : uint32_t // NOLINT(performance-enum-size)
{
None,
Texture,
Text,
Mesh,
Material,
};
struct Metadata
{
Type type;
};
Asset() = default;
/** Directly unpacks from disk to the destination.
*
* @note The destination MUST have at least blob_metadata.unpacked_size bytes available for
* writing, otherwise segfault could occur!
*/
void unpack_blob(BlobMetadata::Tag blob_tag, std::byte *destination);
};
} // namespace Assets

View file

@ -0,0 +1,14 @@
#pragma once
#include <cstdint>
namespace Assets {
enum class CompressionType : uint32_t // NOLINT(performance-enum-size)
{
None,
LZ4,
LZ4HC,
};
}

View file

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

View file

@ -0,0 +1,166 @@
#include <asset_parser/assets/texture.hpp>
#include <lz4.h>
#include <nlohmann/json.hpp>
namespace Assets {
/* static */ void TextureAsset::pack(const PackageData &data, const std::filesystem::path &out_path)
{
const auto &[metadata, texture_metadata, pixels] = data;
auto stream = std::ofstream { out_path, std::ios::binary | std::ios::trunc };
if (!stream.is_open())
{
throw std::runtime_error {
std::format("Failed to open ofstream for packing texture at: {}", out_path.string())
};
}
stream.seekp(0);
// NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast)
stream.write((char *)&current_version, sizeof(current_version));
stream.write((char *)&metadata, sizeof(metadata));
stream.write((char *)&texture_metadata, sizeof(texture_metadata));
constexpr auto number_of_blobs = uint32_t { 1 };
stream.write((char *)&number_of_blobs, sizeof(number_of_blobs));
auto pixels_metadata = BlobMetadata {
.tag = BlobMetadata::Tag::color,
.offset = static_cast<size_t>(stream.tellp()) + sizeof(BlobMetadata),
.compression_type = CompressionType::None,
.compressed_size = pixels.size(),
.uncompressed_size = pixels.size(),
};
stream.write((char *)&pixels_metadata, sizeof(pixels_metadata));
stream.write((char *)&pixels[0], static_cast<long>(pixels.size()));
// NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast)
}
TextureAsset::TextureAsset(const std::filesystem::path &path)
{
m_stream = std::ifstream { path, std::ios::binary };
if (!m_stream.is_open())
{
throw std::runtime_error {
std::format("Failed to open ifstream for loading texture asset at: {}", path.string())
};
}
// NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast)
m_stream.read((char *)&version, sizeof(version));
m_stream.read((char *)&m_asset_metadata, sizeof(m_asset_metadata));
m_stream.read((char *)&m_metadata, sizeof(m_metadata));
auto num_blobs = uint32_t {};
m_stream.read((char *)&num_blobs, sizeof(num_blobs));
if (num_blobs != 1)
{
throw std::runtime_error {
std::format("Failed to load texture asset: invalid number of blobs: {}", num_blobs)
};
}
m_stream.read((char *)&m_pixel_blob_metadata, sizeof(m_pixel_blob_metadata));
if (m_pixel_blob_metadata.tag != BlobMetadata::Tag::color)
{
throw std::runtime_error {
std::format(
"Failed to load texture asset: invalid blob tag, expected {}, got {}",
std::to_underlying(BlobMetadata::Tag::color),
std::to_underlying(m_pixel_blob_metadata.tag)
),
};
}
// NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast)
}
void TextureAsset::unpack_blob(
BlobMetadata::Tag tag,
std::byte *destination,
size_t destination_capacity
)
{
if (tag != BlobMetadata::Tag::color)
{
throw std::runtime_error {
std::format("Invalid tag for unpack_blob of TextureAsset: {}", std::to_underlying(tag))
};
}
m_stream.seekg(static_cast<long>(m_pixel_blob_metadata.offset));
switch (m_pixel_blob_metadata.compression_type)
{
case Assets::CompressionType::None:
if (m_pixel_blob_metadata.uncompressed_size != m_pixel_blob_metadata.compressed_size)
{
throw std::runtime_error(
"Failed to unpack blob from TextureAsset: "
"compressed/uncompressed size mismatch for no compression "
"type"
);
}
if (m_pixel_blob_metadata.uncompressed_size > destination_capacity)
{
throw std::runtime_error(
"Failed to unpack blob from TextureAsset: "
"uncompressed_size > destination_capacity, unpacking "
"would result in segfault"
);
}
if (!m_stream.is_open())
{
throw std::runtime_error(
"Failed to unpack blob from TextureAsset: ifstream is "
"closed"
);
}
m_stream.read(
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
(char *)destination,
static_cast<long>(m_pixel_blob_metadata.uncompressed_size)
);
return;
default:
throw std::runtime_error(
std::format(
"Failed to unpack blob from TextureAsset: unsupported "
"compression type: {}",
std::to_underlying(m_pixel_blob_metadata.compression_type)
)
);
}
}
[[nodiscard]] auto TextureAsset::get_asset_metadata() const -> const Asset::Metadata &
{
return m_asset_metadata;
}
[[nodiscard]] auto TextureAsset::get_metadata() const -> const Metadata &
{
return m_metadata;
}
[[nodiscard]] auto TextureAsset::get_blob_metadata(BlobMetadata::Tag tag) const
-> const BlobMetadata &
{
if (tag != BlobMetadata::Tag::color)
{
throw std::runtime_error { std::format(
"Invalid tag for get_blob_metadata of TextureAsset: {}",
std::to_underlying(tag)
) };
}
return m_pixel_blob_metadata;
}
} // namespace Assets

View file

@ -0,0 +1,62 @@
#include <asset_parser/parser.hpp>
#include <format>
#include <fstream>
#include <utility>
namespace Assets {
// void Asset::unpack(std::byte *destination)
// {
// if (!m_stream.is_open())
// {
// throw std::logic_error {
// "Failed to unpack asset: "
// "ifstream is closed",
// };
// }
//
// switch (m_metadata.blob_compression_type)
// {
// case CompressionType::None:
// if (m_metadata.packed_size != m_metadata.unpacked_size)
// {
// throw std::logic_error {
// "Failed to unpack asset: "
// "compression type set to none but packed/unpacked sizes differ",
// };
// }
//
// m_stream.read(
// std::bit_cast<char *>(destination),
// static_cast<long>(m_metadata.packed_size)
// );
// m_stream.close();
//
// case CompressionType::LZ4:
// m_stream.close();
// throw std::logic_error {
// "Failed to unpack asset: "
// "LZ4 compression is not implemented yet",
// };
//
//
// case CompressionType::LZ4HC:
// m_stream.close();
// throw std::logic_error {
// "Failed to unpack asset: "
// "LZ4HC compression is not implemented yet",
// };
//
// default:
// m_stream.close();
// throw std::logic_error {
// std::format(
// "Failed to unpack asset: "
// "Compression type was not recognized: {}",
// std::to_underlying(m_metadata.blob_compression_type)
// ),
// };
// }
// }
} // namespace Assets

View file

@ -1,5 +0,0 @@
add_library_module(assets shader.cpp)
target_link_libraries(assets PUBLIC logger lt_debug)
add_test_module(assets shader.test.cpp)

View file

@ -1,148 +0,0 @@
#include <assets/shader.hpp>
namespace lt::assets {
constexpr auto total_metadata_size = //
sizeof(AssetMetadata::type) //
+ sizeof(AssetMetadata::version) //
+ sizeof(ShaderAsset::Metadata::type) //
+ sizeof(BlobMetadata::tag) //
+ sizeof(BlobMetadata::offset) //
+ sizeof(BlobMetadata::compression_type) //
+ sizeof(BlobMetadata::compressed_size) //
+ sizeof(BlobMetadata::uncompressed_size);
ShaderAsset::ShaderAsset(const std::filesystem::path &path): m_stream(path)
{
ensure(m_stream.is_open(), "Failed to open shader asset at: {}", path.string());
const auto read = [this](auto &field) {
m_stream.read(std::bit_cast<char *>(&field), sizeof(field));
};
m_stream.seekg(0, std::ifstream::end);
const auto file_size = static_cast<size_t>(m_stream.tellg());
ensure(
file_size > total_metadata_size,
"Failed to open shader asset at: {}, file smaller than metadata: {} < {}",
path.string(),
total_metadata_size,
file_size
);
m_stream.seekg(0, std::ifstream::beg);
read(m_asset_metadata.type);
read(m_asset_metadata.version);
read(m_metadata.type);
read(m_code_blob_metadata.tag);
read(m_code_blob_metadata.offset);
read(m_code_blob_metadata.compression_type);
read(m_code_blob_metadata.compressed_size);
read(m_code_blob_metadata.uncompressed_size);
ensure(
m_asset_metadata.type == asset_type_identifier,
"Failed to open shader asset at: {}, incorrect asset type: {} != {}",
path.string(),
m_asset_metadata.type,
asset_type_identifier
);
ensure(
m_asset_metadata.version == current_version,
"Failed to open shader asset at: {}, version mismatch: {} != {}",
path.string(),
m_asset_metadata.version,
current_version
);
ensure(
std::to_underlying(m_metadata.type) <= std::to_underlying(Type::compute),
"Failed to open shader asset at: {}, invalid shader type: {}",
path.string(),
std::to_underlying(m_metadata.type)
);
ensure(
m_code_blob_metadata.tag == std::to_underlying(BlobTag::code),
"Failed to open shader asset at: {}, invalid blob tag: {}",
path.string(),
m_code_blob_metadata.tag
);
ensure(
m_code_blob_metadata.offset + m_code_blob_metadata.compressed_size <= file_size,
"Failed to open shader asset at: {}, file smaller than blob: {} > {} + {}",
path.string(),
file_size,
m_code_blob_metadata.offset,
m_code_blob_metadata.compressed_size
);
}
/* static */ void ShaderAsset::pack(
const std::filesystem::path &destination,
AssetMetadata asset_metadata,
Metadata metadata,
Blob code_blob
)
{
auto stream = std::ofstream {
destination,
std::ios::binary | std::ios::trunc,
};
const auto code_blob_metadata = BlobMetadata {
.tag = std::to_underlying(BlobTag::code),
.offset = total_metadata_size,
.compression_type = CompressionType::none,
.compressed_size = code_blob.size(),
.uncompressed_size = code_blob.size(),
};
ensure(stream.is_open(), "Failed to pack shader asset to {}", destination.string());
const auto write = [&stream](auto &field) {
stream.write(std::bit_cast<char *>(&field), sizeof(field));
};
write(asset_metadata.type);
write(asset_metadata.version);
write(metadata.type);
write(code_blob_metadata.tag);
write(code_blob_metadata.offset);
write(code_blob_metadata.compression_type);
write(code_blob_metadata.compressed_size);
write(code_blob_metadata.uncompressed_size);
stream.write(std::bit_cast<char *>(code_blob.data()), static_cast<long long>(code_blob.size()));
}
void ShaderAsset::unpack_to(BlobTag tag, std::span<std::byte> destination) const
{
ensure(tag == BlobTag::code, "Invalid blob tag for shader asset: {}", std::to_underlying(tag));
ensure(
destination.size() >= m_code_blob_metadata.uncompressed_size,
"Failed to unpack shader blob {} to destination ({}) of size {} since it's smaller "
"than the blobl's uncompressed size: {}",
std::to_underlying(tag),
std::bit_cast<size_t>(destination.data()),
destination.size(),
m_code_blob_metadata.uncompressed_size
);
m_stream.seekg(static_cast<long long>(m_code_blob_metadata.offset));
m_stream.read(
std::bit_cast<char *>(destination.data()),
static_cast<long long>(m_code_blob_metadata.uncompressed_size)
);
}
[[nodiscard]] auto ShaderAsset::unpack(BlobTag tag) const -> Blob
{
ensure(tag == BlobTag::code, "Invalid blob tag for shader asset: {}", std::to_underlying(tag));
auto blob = Blob(m_code_blob_metadata.uncompressed_size);
unpack_to(tag, blob);
return blob;
}
} // namespace lt::assets

View file

@ -1,94 +0,0 @@
#include <assets/shader.hpp>
#include <ranges>
#include <test/test.hpp>
using ::lt::assets::AssetMetadata;
using ::lt::assets::BlobMetadata;
using ::lt::assets::ShaderAsset;
using ::lt::test::Case;
using ::lt::test::expect_eq;
using ::lt::test::expect_throw;
using ::lt::test::expect_true;
using ::lt::test::Suite;
const auto test_data_path = std::filesystem::path { "./data/test_assets" };
const auto tmp_path = std::filesystem::path { "/tmp/lt_assets_tests/" };
Suite raii = "shader_raii"_suite = [] {
std::filesystem::current_path(test_data_path);
std::filesystem::create_directories(tmp_path);
Case { "happy path won't throw" } = [] {
};
Case { "many won't freeze/throw" } = [] {
};
Case { "unhappy path throws" } = [] {
expect_throw([] { ShaderAsset { "random_path" }; });
};
};
// NOLINTNEXTLINE(cppcoreguidelines-interfaces-global-init)
Suite packing = "shader_pack"_suite = [] {
Case { "" } = [] {
const auto out_path = tmp_path / "shader_packing";
auto dummy_blob = lt::assets::Blob {};
for (auto idx : std::views::iota(0, 255))
{
dummy_blob.emplace_back(static_cast<std::byte>(idx));
}
const auto expected_size = //
sizeof(AssetMetadata::type) //
+ sizeof(AssetMetadata::version) //
+ sizeof(ShaderAsset::Metadata::type) //
+ sizeof(BlobMetadata::tag) //
+ sizeof(BlobMetadata::offset) //
+ sizeof(BlobMetadata::compression_type) //
+ sizeof(BlobMetadata::compressed_size) //
+ sizeof(BlobMetadata::uncompressed_size) //
+ dummy_blob.size();
ShaderAsset::pack(
out_path,
lt::assets::AssetMetadata {
.version = lt::assets::current_version,
.type = ShaderAsset::asset_type_identifier,
},
ShaderAsset::Metadata {
.type = ShaderAsset::Type::vertex,
},
std::move(dummy_blob)
);
auto stream = std::ifstream {
out_path,
std::ios::binary,
};
expect_true(stream.is_open());
stream.seekg(0, std::ios::end);
const auto file_size = static_cast<size_t>(stream.tellg());
expect_eq(file_size, expected_size);
stream.close();
auto shader_asset = ShaderAsset { out_path };
const auto &asset_metadata = shader_asset.get_asset_metadata();
expect_eq(asset_metadata.type, ShaderAsset::asset_type_identifier);
expect_eq(asset_metadata.version, lt::assets::current_version);
const auto &metadata = shader_asset.get_metadata();
expect_eq(metadata.type, ShaderAsset::Type::vertex);
auto blob = shader_asset.unpack(ShaderAsset::BlobTag::code);
expect_eq(blob.size(), 255);
for (auto idx : std::views::iota(0, 255))
{
expect_eq(blob[idx], static_cast<std::byte>(idx));
}
};
};

View file

@ -1,3 +0,0 @@
#pragma once
// TO BE DOOO

View file

@ -1,42 +0,0 @@
#pragma once
namespace lt::assets {
using Type_T = std::array<const char, 16>;
using Tag_T = uint8_t;
using Version = uint8_t;
using Blob = std::vector<std::byte>;
constexpr auto current_version = Version { 1u };
enum class CompressionType : uint8_t
{
none,
lz4,
lz4_hc,
};
struct AssetMetadata
{
Version version;
Type_T type;
};
struct BlobMetadata
{
Tag_T tag;
size_t offset;
CompressionType compression_type;
size_t compressed_size;
size_t uncompressed_size;
};
} // namespace lt::assets

View file

@ -1,74 +0,0 @@
#pragma once
#include <assets/metadata.hpp>
namespace lt::assets {
class ShaderAsset
{
public:
static constexpr auto asset_type_identifier = Type_T { "SHADER_________" };
enum class BlobTag : Tag_T
{
code,
};
enum class Type : uint8_t
{
vertex,
fragment,
geometry,
compute,
};
struct Metadata
{
Type type;
};
static void pack(
const std::filesystem::path &destination,
AssetMetadata asset_metadata,
Metadata metadata,
Blob code_blob
);
ShaderAsset(const std::filesystem::path &path);
void unpack_to(BlobTag tag, std::span<std::byte> destination) const;
[[nodiscard]] auto unpack(BlobTag tag) const -> Blob;
[[nodiscard]] auto get_asset_metadata() const -> const AssetMetadata &
{
return m_asset_metadata;
}
[[nodiscard]] auto get_metadata() const -> const Metadata &
{
return m_metadata;
}
[[nodiscard]] auto get_blob_metadata(BlobTag tag) const -> const BlobMetadata &
{
ensure(
tag == BlobTag::code,
"Invalid blob tag for shader asset: {}",
std::to_underlying(tag)
);
return m_code_blob_metadata;
}
private:
AssetMetadata m_asset_metadata {};
Metadata m_metadata {};
BlobMetadata m_code_blob_metadata {};
mutable std::ifstream m_stream;
};
} // namespace lt::assets

View file

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

View file

@ -0,0 +1,91 @@
#pragma once
#include <memory>
namespace lt {
// Ref (Ref)
template<typename t>
using Ref = std::shared_ptr<t>;
template<typename t, typename... Args>
constexpr Ref<t> create_ref(Args &&...args)
{
return std::make_shared<t>(std::forward<Args>(args)...);
}
template<typename t>
constexpr Ref<t> make_ref(t *rawPointer)
{
return std::shared_ptr<t>(rawPointer);
}
// Scope (std::unique_ptr)
template<typename t>
using Scope = std::unique_ptr<t>;
template<typename t, typename... Args>
constexpr std::unique_ptr<t> create_scope(Args &&...args)
{
return std::make_unique<t>(std::forward<Args>(args)...);
}
template<typename t>
constexpr std::unique_ptr<t> make_scope(t *rawPointer)
{
return std::unique_ptr<t>(rawPointer);
}
} // namespace lt
#define lt_win(x) // windows
#define lt_lin(x) // linux
#define lt_mac(x) // mac
enum class Platform : uint8_t
{
windows,
/** Named like so because "linux" is a built-in identifier. */
gnu,
mac,
};
namespace constants {
#if defined(LIGHT_PLATFORM_WINDOWS)
#define lt_win(x)
constexpr auto platform = Platform::windows;
constexpr auto platform_name = "windows";
#undef LIGHT_PLATFORM_WINDOWS
#elif defined(LIGHT_PLATFORM_LINUX)
#define lt_lin(x) x
constexpr auto platform = Platform::gnu;
constexpr auto platform_name = "linux";
#elif defined(LIGHT_PLATFORM_MAC)
#define lt_mac(x) x
constexpr auto platform = Platform::mac;
constexpr auto platform_name = "mac";
#else
#error "Unsupported platform: Unknown"
#endif
} // namespace constants
/* bit-wise */
constexpr auto bit(auto x)
{
return 1 << x;
}
/* token */
#define lt_pair_token_value_to_name(token) { token, #token }
#define lt_pair_token_name_to_value(token) { #token, token }
#define lt_token_name(token) #token

36
modules/base/src/pch.hpp Normal file
View file

@ -0,0 +1,36 @@
#pragma once
#include <base/base.hpp>
/* windows */
#ifdef _WIN32
#define NOMINMAX
#include <Windows.h>
#undef NOMINMAX
#endif
/** Stdlib */
#include <algorithm>
#include <array>
#include <atomic>
#include <bitset>
#include <filesystem>
#include <fstream>
#include <functional>
#include <iostream>
#include <list>
#include <map>
#include <math.h>
#include <memory>
#include <set>
#include <span>
#include <sstream>
#include <string>
#include <string_view>
#include <thread>
#include <time.h>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

View file

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

View file

@ -1,13 +0,0 @@
#pragma once
#include <cstdint>
namespace lt::bitwise {
/* bit-wise */
constexpr auto bit(uint32_t x) -> uint32_t
{
return 1u << x;
}
} // namespace lt::bitwise

View file

@ -1,3 +1,3 @@
add_library_module(camera)
add_library_module(camera camera.cpp scene.cpp)
target_link_libraries(camera INTERFACE 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

@ -0,0 +1,30 @@
#pragma once
#include <camera/scene.hpp>
#include <glm/glm.hpp>
namespace lt {
struct CameraComponent
{
CameraComponent() = default;
CameraComponent(const CameraComponent &) = default;
CameraComponent(SceneCamera _camera, bool _isPrimary = false)
: camera(_camera)
, isPrimary(_isPrimary)
{
}
operator SceneCamera() const
{
return camera;
}
SceneCamera camera;
bool isPrimary {};
};
} // namespace lt

View file

@ -0,0 +1,100 @@
#pragma once
#include <camera/camera.hpp>
namespace lt {
class SceneCamera: public Camera
{
public:
enum class ProjectionType
{
Orthographic = 0,
Perspetcive = 1
};
struct OrthographicSpecification
{
float size;
float near_plane;
float far_plane;
};
struct PerspectiveSpecification
{
float vertical_fov;
float near_plane;
float far_plane;
};
SceneCamera();
void set_viewport_size(unsigned int width, unsigned int height);
void set_projection_type(ProjectionType projection_type);
void set_orthographic_size(float size);
void set_orthographic_far_plane(float far_plane);
void set_orthographic_near_plane(float near_plane);
void set_perspective_vertical_fov(float vertical_fov);
void set_perspective_far_plane(float far_plane);
void set_perspective_near_plane(float near_plane);
[[nodiscard]] auto get_orthographic_size() const -> float
{
return m_orthographic_specification.size;
}
[[nodiscard]] auto get_orthographic_far_plane() const -> float
{
return m_orthographic_specification.far_plane;
}
[[nodiscard]] auto get_orthographic_near_plane() const -> float
{
return m_orthographic_specification.near_plane;
}
[[nodiscard]] auto get_perspective_vertical_fov() const -> float
{
return m_perspective_specification.vertical_fov;
}
[[nodiscard]] auto get_perspective_far_plane() const -> float
{
return m_perspective_specification.far_plane;
}
[[nodiscard]] auto get_perspective_near_plane() const -> float
{
return m_perspective_specification.near_plane;
}
[[nodiscard]] auto get_projection_type() const -> ProjectionType
{
return m_projection_type;
}
private:
OrthographicSpecification m_orthographic_specification;
PerspectiveSpecification m_perspective_specification;
float m_aspect_ratio;
ProjectionType m_projection_type { ProjectionType::Orthographic };
void calculate_projection();
};
} // namespace lt

View file

@ -1,22 +0,0 @@
#pragma once
#include <math/mat4.hpp>
namespace lt::camera::components {
struct PerspectiveCamera
{
float vertical_fov {};
float near_plane {};
float far_plane {};
float aspect_ratio {};
math::vec4 background_color;
bool is_primary {};
};
} // namespace lt::camera::components

View file

@ -0,0 +1,6 @@
#include <camera/camera.hpp>
namespace lt {
}

View file

@ -0,0 +1,89 @@
#include <camera/scene.hpp>
#include <glm/gtc/matrix_transform.hpp>
namespace lt {
SceneCamera::SceneCamera()
: m_orthographic_specification { .size = 1000.0f, .near_plane = -1.0f, .far_plane = 10000.0f }
, m_perspective_specification { .vertical_fov = glm::radians(45.0f),
.near_plane = 0.01f,
.far_plane = 10000.0f }
, m_aspect_ratio(16.0f / 9.0f)
{
calculate_projection();
}
void SceneCamera::set_viewport_size(unsigned int width, unsigned int height)
{
m_aspect_ratio = static_cast<float>(width) / static_cast<float>(height);
calculate_projection();
}
void SceneCamera::set_projection_type(ProjectionType projection_type)
{
m_projection_type = projection_type;
calculate_projection();
}
void SceneCamera::set_orthographic_size(float size)
{
m_orthographic_specification.size = size;
calculate_projection();
}
void SceneCamera::set_orthographic_far_plane(float far_plane)
{
m_orthographic_specification.far_plane = far_plane;
calculate_projection();
}
void SceneCamera::set_orthographic_near_plane(float near_plane)
{
m_orthographic_specification.near_plane = near_plane;
calculate_projection();
}
void SceneCamera::set_perspective_vertical_fov(float vertical_fov)
{
m_perspective_specification.vertical_fov = vertical_fov;
calculate_projection();
}
void SceneCamera::set_perspective_far_plane(float far_plane)
{
m_perspective_specification.far_plane = far_plane;
calculate_projection();
}
void SceneCamera::set_perspective_near_plane(float near_plane)
{
m_perspective_specification.near_plane = near_plane;
calculate_projection();
}
void SceneCamera::calculate_projection()
{
if (m_projection_type == ProjectionType::Orthographic)
{
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
);
}
}
} // namespace lt

View file

@ -1,4 +1,3 @@
add_library_module(lt_debug instrumentor.cpp)
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

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