Compare commits
	
		
			2 commits
		
	
	
		
			main
			...
			ci/libx11_
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| dd11366d03 | |||
| d8ab0f5118 | 
					 304 changed files with 8699 additions and 10029 deletions
				
			
		
							
								
								
									
										5
									
								
								.clangd
									
										
									
									
									
								
							
							
						
						
									
										5
									
								
								.clangd
									
										
									
									
									
								
							| 
						 | 
					@ -1,5 +0,0 @@
 | 
				
			||||||
CompileFlags:
 | 
					 | 
				
			||||||
  DriverMode: cl
 | 
					 | 
				
			||||||
  Add:
 | 
					 | 
				
			||||||
    - /EHsc
 | 
					 | 
				
			||||||
    - /std:c++latest
 | 
					 | 
				
			||||||
							
								
								
									
										37
									
								
								.drone.yml
									
										
									
									
									
								
							
							
						
						
									
										37
									
								
								.drone.yml
									
										
									
									
									
								
							| 
						 | 
					@ -25,13 +25,13 @@ trigger:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
steps:
 | 
					steps:
 | 
				
			||||||
- name: unit tests
 | 
					- name: unit tests
 | 
				
			||||||
  image: ci:latest
 | 
					  image: amd64_gcc_unit_tests:latest
 | 
				
			||||||
  pull: if-not-exists
 | 
					  pull: if-not-exists
 | 
				
			||||||
  commands:
 | 
					  commands:
 | 
				
			||||||
    - ./tools/ci/amd64/gcc/unit_tests.sh
 | 
					    - ./tools/ci/amd64/gcc/unit_tests.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- name: valgrind
 | 
					- name: valgrind
 | 
				
			||||||
  image: ci:latest
 | 
					  image: amd64_gcc_valgrind:latest
 | 
				
			||||||
  pull: if-not-exists
 | 
					  pull: if-not-exists
 | 
				
			||||||
  commands:
 | 
					  commands:
 | 
				
			||||||
    - ./tools/ci/amd64/gcc/valgrind.sh
 | 
					    - ./tools/ci/amd64/gcc/valgrind.sh
 | 
				
			||||||
| 
						 | 
					@ -46,7 +46,7 @@ trigger:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
steps:
 | 
					steps:
 | 
				
			||||||
- name: code coverage
 | 
					- name: code coverage
 | 
				
			||||||
  image: ci:latest
 | 
					  image: amd64_clang_coverage:latest
 | 
				
			||||||
  pull: if-not-exists
 | 
					  pull: if-not-exists
 | 
				
			||||||
  environment:
 | 
					  environment:
 | 
				
			||||||
    CODECOV_TOKEN:
 | 
					    CODECOV_TOKEN:
 | 
				
			||||||
| 
						 | 
					@ -55,13 +55,13 @@ steps:
 | 
				
			||||||
    - ./tools/ci/amd64/clang/coverage.sh
 | 
					    - ./tools/ci/amd64/clang/coverage.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- name: leak sanitizer
 | 
					- name: leak sanitizer
 | 
				
			||||||
  image: ci:latest
 | 
					  image: amd64_clang_lsan:latest
 | 
				
			||||||
  pull: if-not-exists
 | 
					  pull: if-not-exists
 | 
				
			||||||
  commands:
 | 
					  commands:
 | 
				
			||||||
    - ./tools/ci/amd64/clang/lsan.sh
 | 
					    - ./tools/ci/amd64/clang/lsan.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- name: memory sanitizer
 | 
					- name: memory sanitizer
 | 
				
			||||||
  image: ci:latest
 | 
					  image: amd64_clang_msan:latest
 | 
				
			||||||
  pull: if-not-exists
 | 
					  pull: if-not-exists
 | 
				
			||||||
  commands:
 | 
					  commands:
 | 
				
			||||||
    - ./tools/ci/amd64/clang/msan.sh
 | 
					    - ./tools/ci/amd64/clang/msan.sh
 | 
				
			||||||
| 
						 | 
					@ -76,36 +76,18 @@ trigger:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
steps:
 | 
					steps:
 | 
				
			||||||
- name: clang tidy
 | 
					- name: clang tidy
 | 
				
			||||||
  image: ci:latest
 | 
					  image: clang_tidy:latest
 | 
				
			||||||
  pull: if-not-exists
 | 
					  pull: if-not-exists
 | 
				
			||||||
  privileged: true
 | 
					  privileged: true
 | 
				
			||||||
  commands:
 | 
					  commands:
 | 
				
			||||||
    - ./tools/ci/static_analysis/clang_tidy.sh
 | 
					    - ./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
 | 
					- name: clang format
 | 
				
			||||||
  image: ci:latest
 | 
					  image: clang_format:latest
 | 
				
			||||||
  pull: if-not-exists
 | 
					  pull: if-not-exists
 | 
				
			||||||
  commands:
 | 
					  commands:
 | 
				
			||||||
    - ./tools/ci/static_analysis/clang_format.sh
 | 
					    - ./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
 | 
					kind: pipeline
 | 
				
			||||||
type: docker 
 | 
					type: docker 
 | 
				
			||||||
| 
						 | 
					@ -121,7 +103,11 @@ steps:
 | 
				
			||||||
  image: documentation:latest
 | 
					  image: documentation:latest
 | 
				
			||||||
  pull: if-not-exists
 | 
					  pull: if-not-exists
 | 
				
			||||||
  commands:
 | 
					  commands:
 | 
				
			||||||
 | 
					    - pwd
 | 
				
			||||||
    - cd docs
 | 
					    - cd docs
 | 
				
			||||||
 | 
					    - mkdir generated
 | 
				
			||||||
 | 
					    - touch generated/changelogs.rst
 | 
				
			||||||
 | 
					    - touch generated/api.rst
 | 
				
			||||||
    - sphinx-build -M html . .
 | 
					    - sphinx-build -M html . .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - rm -rf /light_docs_dev/*
 | 
					    - rm -rf /light_docs_dev/*
 | 
				
			||||||
| 
						 | 
					@ -151,4 +137,3 @@ steps:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - rm -rf /light_docs/*
 | 
					    - rm -rf /light_docs/*
 | 
				
			||||||
    - mv ./html/* /light_docs/
 | 
					    - mv ./html/* /light_docs/
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,42 @@
 | 
				
			||||||
cmake_minimum_required(VERSION 3.14)
 | 
					cmake_minimum_required(VERSION 3.14)
 | 
				
			||||||
project(Light)
 | 
					project(Light)
 | 
				
			||||||
 | 
					set(CMAKE_CXX_STANDARD 23)
 | 
				
			||||||
 | 
					set(CMAKE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
include(${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/functions.cmake)
 | 
					include(CheckCXXSourceCompiles)
 | 
				
			||||||
include(${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/definitions.cmake)
 | 
					include(${CMAKE_DIR}/functions.cmake)
 | 
				
			||||||
include(${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/options.cmake)
 | 
					include(${CMAKE_DIR}/definitions.cmake)
 | 
				
			||||||
 | 
					include(${CMAKE_DIR}/dependencies.cmake)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_option(ENABLE_UNIT_TESTS "Enables the building of the unit test modules")
 | 
				
			||||||
 | 
					add_option(ENABLE_FUZZ_TESTS "Enables the building of the fuzz test modules")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_option(ENABLE_STATIC_ANALYSIS "Makes clang-tidy checks mandatory for compilation")
 | 
				
			||||||
 | 
					if (ENABLE_STATIC_ANALYSIS)
 | 
				
			||||||
 | 
					    set(CMAKE_CXX_CLANG_TIDY "clang-tidy;--warnings-as-errors=*;--allow-no-checks")
 | 
				
			||||||
 | 
					endif ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_option(ENABLE_LLVM_COVERAGE "Enables the code coverage instrumentation for clang")
 | 
				
			||||||
 | 
					if(ENABLE_LLVM_COVERAGE)
 | 
				
			||||||
 | 
					    if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
 | 
				
			||||||
 | 
					        message(FATAL_ERROR "ENABLE_LLVM_COVERAGE only supports the clang compiler")
 | 
				
			||||||
 | 
					    endif ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Check for libc++
 | 
				
			||||||
 | 
					    check_cxx_source_compiles("
 | 
				
			||||||
 | 
					        #include <string>
 | 
				
			||||||
 | 
					        #ifdef _LIBCPP_VERSION
 | 
				
			||||||
 | 
					        int main() { return 0; }
 | 
				
			||||||
 | 
					        #else
 | 
				
			||||||
 | 
					        #error Not using libc++
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
 | 
					    " USING_LIBCXX)
 | 
				
			||||||
 | 
					    if(NOT USING_LIBCXX)
 | 
				
			||||||
 | 
					        message(FATAL_ERROR "ENABLE_LLVM_COVERAGE requires libc++, please compile with -stdlib=libc++")
 | 
				
			||||||
 | 
					    endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
 | 
				
			||||||
 | 
					    add_link_options(-fprofile-instr-generate -fcoverage-mapping)
 | 
				
			||||||
 | 
					endif ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/modules)
 | 
					add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/modules)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										128
									
								
								CODE_OF_CONDUCT.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								CODE_OF_CONDUCT.md
									
										
									
									
									
										Normal 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.
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,6 @@
 | 
				
			||||||
# Light
 | 
					# Light
 | 
				
			||||||
See docs.light7734.com for a comprehensive project documentation
 | 
					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 person’s mind so soon and so easily? I tell you, you must not expect it.” —Epictetus, Discourses 1.15.7-8
 | 
					<!---FUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUCK
 | 
				
			||||||
 | 
					MEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
 | 
				
			||||||
 | 
					AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!!!!!!!!-->
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										63
									
								
								conanfile.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								conanfile.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,63 @@
 | 
				
			||||||
 | 
					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_unit_tests": [True, False],
 | 
				
			||||||
 | 
					        "enable_fuzz_tests": [True, False],
 | 
				
			||||||
 | 
					        "enable_llvm_coverage": [True, False],
 | 
				
			||||||
 | 
					        "enable_static_analysis": [True, False],
 | 
				
			||||||
 | 
					        "use_mold": [True, False],
 | 
				
			||||||
 | 
					        "export_compile_commands": [True, False],
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    default_options = {
 | 
				
			||||||
 | 
					        "enable_unit_tests": True,
 | 
				
			||||||
 | 
					        "enable_fuzz_tests": False,
 | 
				
			||||||
 | 
					        "enable_llvm_coverage": False,
 | 
				
			||||||
 | 
					        "enable_static_analysis": False,
 | 
				
			||||||
 | 
					        "use_mold": False,
 | 
				
			||||||
 | 
					        "export_compile_commands": True,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def requirements(self):
 | 
				
			||||||
 | 
					        self.requires("imgui/1.92.0-docking")
 | 
				
			||||||
 | 
					        self.requires("entt/3.15.0")
 | 
				
			||||||
 | 
					        self.requires("stb/cci.20240531")
 | 
				
			||||||
 | 
					        self.requires("yaml-cpp/0.8.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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.options.use_mold:
 | 
				
			||||||
 | 
					            tc.cache_variables["CMAKE_LINKER_TYPE"] = "MOLD"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tc.cache_variables["CMAKE_EXPORT_COMPILE_COMMANDS"] = self.options.export_compile_commands
 | 
				
			||||||
 | 
					        tc.cache_variables["ENABLE_UNIT_TESTS"] = self.options.enable_unit_tests
 | 
				
			||||||
 | 
					        tc.cache_variables["ENABLE_FUZZ_TESTS"] = self.options.enable_fuzz_tests
 | 
				
			||||||
 | 
					        tc.cache_variables["ENABLE_LLVM_COVERAGE"] = self.options.enable_llvm_coverage
 | 
				
			||||||
 | 
					        tc.cache_variables["ENABLE_STATIC_ANALYSIS"] = self.options.enable_static_analysis
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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()
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								data/assets/shaders/quads/ps.asset
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/assets/shaders/quads/ps.asset
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										10
									
								
								data/assets/shaders/quads/ps.glsl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								data/assets/shaders/quads/ps.glsl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					#version 440 core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					in vec4 vso_FragmentColor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out vec4 fso_FragmentColor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void main()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						fso_FragmentColor = vso_FragmentColor;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								data/assets/shaders/quads/vs.asset
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/assets/shaders/quads/vs.asset
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										17
									
								
								data/assets/shaders/quads/vs.glsl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								data/assets/shaders/quads/vs.glsl
									
										
									
									
									
										Normal 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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								data/assets/shaders/texture/ps.asset
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/assets/shaders/texture/ps.asset
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										12
									
								
								data/assets/shaders/texture/ps.glsl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								data/assets/shaders/texture/ps.glsl
									
										
									
									
									
										Normal 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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								data/assets/shaders/texture/vs.asset
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/assets/shaders/texture/vs.asset
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										19
									
								
								data/assets/shaders/texture/vs.glsl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								data/assets/shaders/texture/vs.glsl
									
										
									
									
									
										Normal 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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								data/assets/shaders/tinted_texture/ps.asset
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/assets/shaders/tinted_texture/ps.asset
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										14
									
								
								data/assets/shaders/tinted_texture/ps.glsl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								data/assets/shaders/tinted_texture/ps.glsl
									
										
									
									
									
										Normal 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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								data/assets/shaders/tinted_texture/vs.asset
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/assets/shaders/tinted_texture/vs.asset
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										21
									
								
								data/assets/shaders/tinted_texture/vs.glsl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								data/assets/shaders/tinted_texture/vs.glsl
									
										
									
									
									
										Normal 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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
The quick brown fox jumps over the lazy dog
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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.
										
									
								
							| 
						 | 
					@ -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
									
								
							
							
						
						
									
										48
									
								
								default_gui_layout.ini
									
										
									
									
									
										Normal 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								docs/.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								docs/.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,5 +1,3 @@
 | 
				
			||||||
_build/
 | 
					_build/
 | 
				
			||||||
generated/
 | 
					generated/
 | 
				
			||||||
html/
 | 
					 | 
				
			||||||
xml/
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,86 +0,0 @@
 | 
				
			||||||
TARGET = ./
 | 
					 | 
				
			||||||
INPUT = "../modules"
 | 
					 | 
				
			||||||
RECURSIVE = YES
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
PROJECT_NAME           = "Light"
 | 
					 | 
				
			||||||
JAVADOC_AUTOBRIEF      = YES
 | 
					 | 
				
			||||||
JAVADOC_BANNER         = YES
 | 
					 | 
				
			||||||
GENERATE_XML           = YES
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
EXTRACT_PRIVATE = NO
 | 
					 | 
				
			||||||
EXTRACT_STATIC = NO
 | 
					 | 
				
			||||||
EXTRACT_LOCAL_CLASSES = NO
 | 
					 | 
				
			||||||
HIDE_UNDOC_RELATIONS = YES
 | 
					 | 
				
			||||||
HAVE_DOT = NO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
GENERATE_TODOLIST      = NO
 | 
					 | 
				
			||||||
GENERATE_HTML          = NO
 | 
					 | 
				
			||||||
GENERATE_DOCSET        = NO
 | 
					 | 
				
			||||||
GENERATE_HTMLHELP      = NO
 | 
					 | 
				
			||||||
GENERATE_CHI           = NO
 | 
					 | 
				
			||||||
GENERATE_QHP           = NO
 | 
					 | 
				
			||||||
GENERATE_ECLIPSEHELP   = NO
 | 
					 | 
				
			||||||
GENERATE_TREEVIEW      = NO
 | 
					 | 
				
			||||||
GENERATE_LATEX         = NO
 | 
					 | 
				
			||||||
GENERATE_RTF           = NO
 | 
					 | 
				
			||||||
GENERATE_MAN           = NO
 | 
					 | 
				
			||||||
GENERATE_DOCBOOK       = NO
 | 
					 | 
				
			||||||
GENERATE_AUTOGEN_DEF   = NO
 | 
					 | 
				
			||||||
GENERATE_SQLITE3       = NO
 | 
					 | 
				
			||||||
GENERATE_PERLMOD       = NO
 | 
					 | 
				
			||||||
GENERATE_TAGFILE       = NO
 | 
					 | 
				
			||||||
GENERATE_LEGEND        = NO
 | 
					 | 
				
			||||||
GENERATE_TESTLIST      = NO
 | 
					 | 
				
			||||||
GENERATE_BUGLIST       = NO
 | 
					 | 
				
			||||||
GENERATE_DEPRECATEDLIST= NO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
FILE_PATTERNS          = *.c \
 | 
					 | 
				
			||||||
                         *.cc \
 | 
					 | 
				
			||||||
                         *.cxx \
 | 
					 | 
				
			||||||
                         *.cxxm \
 | 
					 | 
				
			||||||
                         *.cpp \
 | 
					 | 
				
			||||||
                         *.cppm \
 | 
					 | 
				
			||||||
                         *.ccm \
 | 
					 | 
				
			||||||
                         *.c++ \
 | 
					 | 
				
			||||||
                         *.c++m \
 | 
					 | 
				
			||||||
                         *.java \
 | 
					 | 
				
			||||||
                         *.ii \
 | 
					 | 
				
			||||||
                         *.ixx \
 | 
					 | 
				
			||||||
                         *.ipp \
 | 
					 | 
				
			||||||
                         *.i++ \
 | 
					 | 
				
			||||||
                         *.inl \
 | 
					 | 
				
			||||||
                         *.idl \
 | 
					 | 
				
			||||||
                         *.ddl \
 | 
					 | 
				
			||||||
                         *.odl \
 | 
					 | 
				
			||||||
                         *.h \
 | 
					 | 
				
			||||||
                         *.hh \
 | 
					 | 
				
			||||||
                         *.hxx \
 | 
					 | 
				
			||||||
                         *.hpp \
 | 
					 | 
				
			||||||
                         *.h++ \
 | 
					 | 
				
			||||||
                         *.l \
 | 
					 | 
				
			||||||
                         *.cs \
 | 
					 | 
				
			||||||
                         *.d \
 | 
					 | 
				
			||||||
                         *.php \
 | 
					 | 
				
			||||||
                         *.php4 \
 | 
					 | 
				
			||||||
                         *.php5 \
 | 
					 | 
				
			||||||
                         *.phtml \
 | 
					 | 
				
			||||||
                         *.inc \
 | 
					 | 
				
			||||||
                         *.m \
 | 
					 | 
				
			||||||
                         *.markdown \
 | 
					 | 
				
			||||||
                         *.md \
 | 
					 | 
				
			||||||
                         *.mm \
 | 
					 | 
				
			||||||
                         *.dox \
 | 
					 | 
				
			||||||
                         *.py \
 | 
					 | 
				
			||||||
                         *.pyw \
 | 
					 | 
				
			||||||
                         *.f90 \
 | 
					 | 
				
			||||||
                         *.f95 \
 | 
					 | 
				
			||||||
                         *.f03 \
 | 
					 | 
				
			||||||
                         *.f08 \
 | 
					 | 
				
			||||||
                         *.f18 \
 | 
					 | 
				
			||||||
                         *.f \
 | 
					 | 
				
			||||||
                         *.for \
 | 
					 | 
				
			||||||
                         *.vhd \
 | 
					 | 
				
			||||||
                         *.vhdl \
 | 
					 | 
				
			||||||
                         *.ucf \
 | 
					 | 
				
			||||||
                         *.qsf \
 | 
					 | 
				
			||||||
                         *.ice
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,17 +0,0 @@
 | 
				
			||||||
Application
 | 
					 | 
				
			||||||
===================================================================================================
 | 
					 | 
				
			||||||
.. toctree::
 | 
					 | 
				
			||||||
   :maxdepth: 3
 | 
					 | 
				
			||||||
   :caption: App
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Functions
 | 
					 | 
				
			||||||
---------------------------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
.. doxygenfunction:: main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Classes
 | 
					 | 
				
			||||||
---------------------------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
.. doxygenclass:: lt::app::ISystem
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. doxygenstruct:: lt::app::TickInfo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. doxygenstruct:: lt::app::TickResult
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,13 +0,0 @@
 | 
				
			||||||
Renderer
 | 
					 | 
				
			||||||
===================================================================================================
 | 
					 | 
				
			||||||
.. toctree::
 | 
					 | 
				
			||||||
   :maxdepth: 3
 | 
					 | 
				
			||||||
   :caption: App
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Classes
 | 
					 | 
				
			||||||
---------------------------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
.. doxygenenum:: lt::renderer::Api
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. doxygenclass:: lt::renderer::System
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. doxygenstruct:: lt::renderer::components::Sprite
 | 
					 | 
				
			||||||
							
								
								
									
										14
									
								
								docs/conf.py
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								docs/conf.py
									
										
									
									
									
								
							| 
						 | 
					@ -13,21 +13,13 @@ author = 'light7734'
 | 
				
			||||||
# -- General configuration ---------------------------------------------------
 | 
					# -- General configuration ---------------------------------------------------
 | 
				
			||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
 | 
					# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extensions = ['breathe']
 | 
					extensions = []
 | 
				
			||||||
 | 
					 | 
				
			||||||
breathe_projects = {"Light": "./xml"}
 | 
					 | 
				
			||||||
breathe_default_project = "Light"
 | 
					 | 
				
			||||||
breathe_default_members = ()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Tell sphinx what the primary language being documented is.
 | 
					 | 
				
			||||||
primary_domain = 'cpp'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Tell sphinx what the pygments highlight language should be.
 | 
					 | 
				
			||||||
highlight_language = 'cpp'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
templates_path = ['_templates']
 | 
					templates_path = ['_templates']
 | 
				
			||||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
 | 
					exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# -- Options for HTML output -------------------------------------------------
 | 
					# -- Options for HTML output -------------------------------------------------
 | 
				
			||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
 | 
					# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										68
									
								
								docs/generate_changelog.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								docs/generate_changelog.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					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')
 | 
				
			||||||
| 
						 | 
					@ -23,10 +23,10 @@
 | 
				
			||||||
   guidelines/conventions.rst
 | 
					   guidelines/conventions.rst
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. toctree::
 | 
					.. toctree::
 | 
				
			||||||
   :maxdepth: 3
 | 
					   :maxdepth: 2
 | 
				
			||||||
   :caption: API
 | 
					   :caption: Generated Docs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   api/app.rst
 | 
					   generated/api.rst
 | 
				
			||||||
   api/renderer.rst
 | 
					   generated/changelog.rst
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										35
									
								
								docs/make.bat
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								docs/make.bat
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					@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
 | 
				
			||||||
| 
						 | 
					@ -1,22 +1,20 @@
 | 
				
			||||||
# engine
 | 
					# engine
 | 
				
			||||||
add_subdirectory(./std)
 | 
					add_subdirectory(./base)
 | 
				
			||||||
add_subdirectory(./bitwise)
 | 
					 | 
				
			||||||
add_subdirectory(./env)
 | 
					 | 
				
			||||||
add_subdirectory(./memory)
 | 
					 | 
				
			||||||
add_subdirectory(./time)
 | 
					add_subdirectory(./time)
 | 
				
			||||||
add_subdirectory(./logger)
 | 
					add_subdirectory(./logger)
 | 
				
			||||||
add_subdirectory(./debug)
 | 
					add_subdirectory(./debug)
 | 
				
			||||||
add_subdirectory(./math)
 | 
					add_subdirectory(./math)
 | 
				
			||||||
# 
 | 
					# 
 | 
				
			||||||
add_subdirectory(./asset_baker)
 | 
					add_subdirectory(./asset_baker)
 | 
				
			||||||
add_subdirectory(./assets)
 | 
					add_subdirectory(./asset_parser)
 | 
				
			||||||
 | 
					# add_subdirectory(./asset_manager)
 | 
				
			||||||
# 
 | 
					# 
 | 
				
			||||||
add_subdirectory(./camera)
 | 
					add_subdirectory(./camera)
 | 
				
			||||||
add_subdirectory(./input)
 | 
					add_subdirectory(./input)
 | 
				
			||||||
# add_subdirectory(./ui)
 | 
					# add_subdirectory(./ui)
 | 
				
			||||||
# 
 | 
					# 
 | 
				
			||||||
add_subdirectory(./surface)
 | 
					add_subdirectory(./surface)
 | 
				
			||||||
add_subdirectory(./renderer)
 | 
					# add_subdirectory(./renderer)
 | 
				
			||||||
add_subdirectory(./ecs)
 | 
					add_subdirectory(./ecs)
 | 
				
			||||||
# 
 | 
					# 
 | 
				
			||||||
add_subdirectory(./app)
 | 
					add_subdirectory(./app)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,2 @@
 | 
				
			||||||
add_library_module(app application.cpp)
 | 
					add_library_module(app application.cpp)
 | 
				
			||||||
target_link_libraries(
 | 
					target_link_libraries(app PRIVATE lt_debug)
 | 
				
			||||||
    app
 | 
					 | 
				
			||||||
    PUBLIC memory
 | 
					 | 
				
			||||||
    PRIVATE lt_debug)
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,5 @@
 | 
				
			||||||
#include <app/application.hpp>
 | 
					#include <app/application.hpp>
 | 
				
			||||||
#include <app/system.hpp>
 | 
					#include <app/system.hpp>
 | 
				
			||||||
#include <memory/reference.hpp>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace lt::app {
 | 
					namespace lt::app {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,16 +9,10 @@ void Application::game_loop()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		for (auto &system : m_systems)
 | 
							for (auto &system : m_systems)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			const auto &last_tick = system->get_last_tick_result();
 | 
								if (system->tick())
 | 
				
			||||||
			const auto now = std::chrono::steady_clock::now();
 | 
								{
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
			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)
 | 
							for (auto &system : m_systems_to_be_registered)
 | 
				
			||||||
| 
						 | 
					@ -42,12 +35,12 @@ void Application::game_loop()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Application::register_system(memory::Ref<app::ISystem> system)
 | 
					void Application::register_system(Ref<app::ISystem> system)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	m_systems.emplace_back(std::move(system));
 | 
						m_systems.emplace_back(std::move(system));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Application::unregister_system(memory::Ref<app::ISystem> system)
 | 
					void Application::unregister_system(Ref<app::ISystem> system)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	m_systems_to_be_unregistered.emplace_back(std::move(system));
 | 
						m_systems_to_be_unregistered.emplace_back(std::move(system));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,10 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <memory/reference.hpp>
 | 
					 | 
				
			||||||
#include <memory/scope.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace lt::app {
 | 
					namespace lt::app {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ISystem;
 | 
					class ISystem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern memory::Scope<class Application> create_application();
 | 
					extern Scope<class Application> create_application();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** The main application class.
 | 
					/** The main application class.
 | 
				
			||||||
 * Think of this like an aggregate of systems, you register systems through this interface.
 | 
					 * Think of this like an aggregate of systems, you register systems through this interface.
 | 
				
			||||||
| 
						 | 
					@ -28,19 +25,19 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void game_loop();
 | 
						void game_loop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void register_system(memory::Ref<app::ISystem> system);
 | 
						void register_system(Ref<app::ISystem> system);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void unregister_system(memory::Ref<app::ISystem> system);
 | 
						void unregister_system(Ref<app::ISystem> system);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
	Application() = default;
 | 
						Application() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	std::vector<memory::Ref<app::ISystem>> m_systems;
 | 
						std::vector<Ref<app::ISystem>> m_systems;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::vector<memory::Ref<app::ISystem>> m_systems_to_be_unregistered;
 | 
						std::vector<Ref<app::ISystem>> m_systems_to_be_unregistered;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::vector<memory::Ref<app::ISystem>> m_systems_to_be_registered;
 | 
						std::vector<Ref<app::ISystem>> m_systems_to_be_registered;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,6 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <app/application.hpp>
 | 
					#include <app/application.hpp>
 | 
				
			||||||
#include <logger/logger.hpp>
 | 
					 | 
				
			||||||
#include <memory/scope.hpp>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
auto main(int argc, char *argv[]) -> int32_t
 | 
					auto main(int argc, char *argv[]) -> int32_t
 | 
				
			||||||
try
 | 
					try
 | 
				
			||||||
| 
						 | 
					@ -10,7 +8,8 @@ try
 | 
				
			||||||
	std::ignore = argc;
 | 
						std::ignore = argc;
 | 
				
			||||||
	std::ignore = argv;
 | 
						std::ignore = argv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto application = lt::memory::Scope<lt::app::Application> {};
 | 
						auto application = lt::Scope<lt::app::Application> {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	application = lt::app::create_application();
 | 
						application = lt::app::create_application();
 | 
				
			||||||
	if (!application)
 | 
						if (!application)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
| 
						 | 
					@ -22,7 +21,7 @@ try
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
catch (const std::exception &exp)
 | 
					catch (const std::exception &exp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	lt::log::critical("Terminating due to uncaught exception:");
 | 
						log_crt("Terminating due to uncaught exception:");
 | 
				
			||||||
	lt::log::critical("\texception.what(): {}", exp.what());
 | 
						log_crt("\texception.what(): {}", exp.what());
 | 
				
			||||||
	return EXIT_FAILURE;
 | 
						return EXIT_FAILURE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,90 +1,7 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <chrono>
 | 
					 | 
				
			||||||
#include <logger/logger.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace lt::app {
 | 
					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
 | 
					class ISystem
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
| 
						 | 
					@ -104,9 +21,7 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	virtual void on_unregister() = 0;
 | 
						virtual void on_unregister() = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	virtual void tick(TickInfo tick) = 0;
 | 
						virtual auto tick() -> bool = 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] virtual auto get_last_tick_result() const -> const TickResult & = 0;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace lt::app
 | 
					} // namespace lt::app
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,10 @@
 | 
				
			||||||
add_library_module(libasset_baker bakers.cpp)
 | 
					add_executable_module(
 | 
				
			||||||
target_link_libraries(libasset_baker PUBLIC assets logger lt_debug tbb)
 | 
					    asset_baker entrypoint/baker.cpp
 | 
				
			||||||
add_test_module(libasset_baker bakers.test.cpp)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_executable_module(asset_baker entrypoint/baker.cpp)
 | 
					target_link_libraries(
 | 
				
			||||||
target_link_libraries(asset_baker PRIVATE libasset_baker)
 | 
					    asset_baker
 | 
				
			||||||
 | 
					    PRIVATE asset_parser
 | 
				
			||||||
 | 
					    PRIVATE stb::stb
 | 
				
			||||||
 | 
					    PRIVATE logger
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,2 +0,0 @@
 | 
				
			||||||
#include <asset_baker/bakers.hpp>
 | 
					 | 
				
			||||||
#include <test/test.hpp>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,69 @@
 | 
				
			||||||
#include <asset_baker/bakers.hpp>
 | 
					#include <asset_baker/bakers.hpp>
 | 
				
			||||||
#include <assets/shader.hpp>
 | 
					#include <asset_parser/assets/text.hpp>
 | 
				
			||||||
 | 
					#include <asset_parser/assets/texture.hpp>
 | 
				
			||||||
 | 
					#include <asset_parser/parser.hpp>
 | 
				
			||||||
 | 
					#include <filesystem>
 | 
				
			||||||
#include <logger/logger.hpp>
 | 
					#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
 | 
					auto main(int argc, char *argv[]) -> int32_t
 | 
				
			||||||
try
 | 
					try
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -19,24 +81,20 @@ try
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const auto &in_path = directory_iterator.path();
 | 
							const auto &in_path = directory_iterator.path();
 | 
				
			||||||
		const auto out_path = std::format("{}.asset", in_path.c_str());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (in_path.extension() == ".vert")
 | 
							auto out_path = in_path;
 | 
				
			||||||
		{
 | 
							out_path.replace_extension(".asset");
 | 
				
			||||||
			bake_shader(in_path, out_path, lt::assets::ShaderAsset::Type::vertex);
 | 
					
 | 
				
			||||||
		}
 | 
							try_packing_texture(in_path, out_path);
 | 
				
			||||||
		else if (in_path.extension() == ".frag")
 | 
							try_packing_text(in_path, out_path);
 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			bake_shader(in_path, out_path, lt::assets::ShaderAsset::Type::fragment);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return EXIT_SUCCESS;
 | 
						return EXIT_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
catch (const std::exception &exp)
 | 
					catch (const std::exception &exp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	lt::log::critical("Terminating due to uncaught exception:");
 | 
						log_crt("Terminating due to uncaught exception:");
 | 
				
			||||||
	lt::log::critical("\texception.what: {}:", exp.what());
 | 
						log_crt("\texception.what: {}:", exp.what());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return EXIT_FAILURE;
 | 
						return EXIT_FAILURE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,65 +1,184 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <assets/shader.hpp>
 | 
					#include <asset_parser/assets/text.hpp>
 | 
				
			||||||
 | 
					#include <asset_parser/assets/texture.hpp>
 | 
				
			||||||
 | 
					#include <filesystem>
 | 
				
			||||||
#include <logger/logger.hpp>
 | 
					#include <logger/logger.hpp>
 | 
				
			||||||
 | 
					#include <string_view>
 | 
				
			||||||
 | 
					#include <unordered_set>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline void bake_shader(
 | 
					#define STB_IMAGE_IMPLEMENTATION
 | 
				
			||||||
    const std::filesystem::path &in_path,
 | 
					#include <stb_image.h>
 | 
				
			||||||
    const std::filesystem::path &out_path,
 | 
					
 | 
				
			||||||
    lt::assets::ShaderAsset::Type type
 | 
					namespace lt {
 | 
				
			||||||
)
 | 
					
 | 
				
			||||||
 | 
					class Loader
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	using lt::assets::ShaderAsset;
 | 
					public:
 | 
				
			||||||
	using enum lt::assets::ShaderAsset::Type;
 | 
						[[nodiscard]] virtual auto get_name() const -> std::string_view = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto glsl_path = in_path.string();
 | 
						Loader() = default;
 | 
				
			||||||
	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.
 | 
						Loader(Loader &&) = default;
 | 
				
			||||||
	// 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);
 | 
						Loader(const Loader &) = delete;
 | 
				
			||||||
	lt::ensure(
 | 
					 | 
				
			||||||
	    stream.is_open(),
 | 
					 | 
				
			||||||
	    "Failed to open compiled {} shader at: {}",
 | 
					 | 
				
			||||||
	    type == vertex ? "vert" : "frag",
 | 
					 | 
				
			||||||
	    spv_path
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stream.seekg(0, std::ios::end);
 | 
						auto operator=(Loader &&) -> Loader & = default;
 | 
				
			||||||
	const auto size = stream.tellg();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto bytes = std::vector<std::byte>(size);
 | 
						auto operator=(const Loader &) -> Loader & = delete;
 | 
				
			||||||
	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(
 | 
						virtual ~Loader() = default;
 | 
				
			||||||
	    out_path,
 | 
					
 | 
				
			||||||
	    lt::assets::AssetMetadata {
 | 
					private:
 | 
				
			||||||
	        .version = lt::assets::current_version,
 | 
					};
 | 
				
			||||||
	        .type = ShaderAsset::asset_type_identifier,
 | 
					
 | 
				
			||||||
	    },
 | 
					class TextureLoader: public Loader
 | 
				
			||||||
	    ShaderAsset::Metadata {
 | 
					{
 | 
				
			||||||
	        .type = type,
 | 
					public:
 | 
				
			||||||
	    },
 | 
						TextureLoader() = default;
 | 
				
			||||||
	    std::move(bytes)
 | 
					
 | 
				
			||||||
	);
 | 
						[[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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										9
									
								
								modules/asset_manager/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								modules/asset_manager/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					add_library_module(asset_manager 
 | 
				
			||||||
 | 
					    asset_manager.cpp
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					target_link_libraries(
 | 
				
			||||||
 | 
					  asset_manager
 | 
				
			||||||
 | 
					  PUBLIC asset_parser
 | 
				
			||||||
 | 
					  PRIVATE logger
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										92
									
								
								modules/asset_manager/private/asset_manager.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								modules/asset_manager/private/asset_manager.cpp
									
										
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										78
									
								
								modules/asset_manager/public/asset_manager.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								modules/asset_manager/public/asset_manager.hpp
									
										
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										11
									
								
								modules/asset_parser/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								modules/asset_parser/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					add_library_module(asset_parser 
 | 
				
			||||||
 | 
					    parser.cpp
 | 
				
			||||||
 | 
					    assets/texture.cpp
 | 
				
			||||||
 | 
					    assets/text.cpp
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					target_link_libraries(
 | 
				
			||||||
 | 
					    asset_parser
 | 
				
			||||||
 | 
					    PRIVATE LZ4::lz4_static 
 | 
				
			||||||
 | 
					    PRIVATE logger
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
#include <asset_parser/assets/text.hpp>
 | 
					#include <asset_parser/assets/text.hpp>
 | 
				
			||||||
 | 
					#include <lz4.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Assets {
 | 
					namespace Assets {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										165
									
								
								modules/asset_parser/private/assets/texture.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								modules/asset_parser/private/assets/texture.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,165 @@
 | 
				
			||||||
 | 
					#include <asset_parser/assets/texture.hpp>
 | 
				
			||||||
 | 
					#include <lz4.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 *)¤t_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
 | 
				
			||||||
							
								
								
									
										62
									
								
								modules/asset_parser/private/parser.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								modules/asset_parser/private/parser.cpp
									
										
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										58
									
								
								modules/asset_parser/public/assets/text.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								modules/asset_parser/public/assets/text.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,58 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <asset_parser/compressors/compressors.hpp>
 | 
				
			||||||
 | 
					#include <asset_parser/parser.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
 | 
				
			||||||
							
								
								
									
										64
									
								
								modules/asset_parser/public/assets/texture.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								modules/asset_parser/public/assets/texture.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,64 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <asset_parser/compressors/compressors.hpp>
 | 
				
			||||||
 | 
					#include <asset_parser/parser.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
 | 
				
			||||||
							
								
								
									
										14
									
								
								modules/asset_parser/public/compressors/compressors.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								modules/asset_parser/public/compressors/compressors.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Assets {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum class CompressionType : uint32_t // NOLINT(performance-enum-size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						None,
 | 
				
			||||||
 | 
						LZ4,
 | 
				
			||||||
 | 
						LZ4HC,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										68
									
								
								modules/asset_parser/public/parser.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								modules/asset_parser/public/parser.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <asset_parser/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
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TO BE DOOO
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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
 | 
					 | 
				
			||||||
							
								
								
									
										2
									
								
								modules/base/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								modules/base/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					add_library_module(base)
 | 
				
			||||||
 | 
					target_precompile_headers(base INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/private/pch.hpp)
 | 
				
			||||||
							
								
								
									
										36
									
								
								modules/base/private/pch.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								modules/base/private/pch.hpp
									
										
									
									
									
										Normal 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>
 | 
				
			||||||
							
								
								
									
										91
									
								
								modules/base/public/base.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								modules/base/public/base.hpp
									
										
									
									
									
										Normal 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
 | 
				
			||||||
| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
add_library_module(bitwise)
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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 math)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										6
									
								
								modules/camera/private/camera.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								modules/camera/private/camera.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					#include <camera/camera.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										83
									
								
								modules/camera/private/scene.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								modules/camera/private/scene.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,83 @@
 | 
				
			||||||
 | 
					#include <camera/scene.hpp>
 | 
				
			||||||
 | 
					#include <math/algebra.hpp>
 | 
				
			||||||
 | 
					#include <math/trig.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SceneCamera::SceneCamera()
 | 
				
			||||||
 | 
					    : m_orthographic_specification { .size = 1000.0f, .near_plane = -1.0f, .far_plane = 10000.0f }
 | 
				
			||||||
 | 
					    , m_perspective_specification { .vertical_fov = math::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()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// TODO(Light): implement ortho perspective
 | 
				
			||||||
 | 
						if (m_projection_type == ProjectionType::Orthographic)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// throw std::runtime_error { "ortho perspective not supported yet" };
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// defaults to perspective for now...
 | 
				
			||||||
 | 
						m_projection = math::perspective(
 | 
				
			||||||
 | 
						    m_perspective_specification.vertical_fov,
 | 
				
			||||||
 | 
						    m_aspect_ratio,
 | 
				
			||||||
 | 
						    m_perspective_specification.near_plane,
 | 
				
			||||||
 | 
						    m_perspective_specification.far_plane
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
							
								
								
									
										35
									
								
								modules/camera/public/camera.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								modules/camera/public/camera.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <math/mat4.hpp>
 | 
				
			||||||
 | 
					#include <math/vec4.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Camera
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						Camera() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto get_projection() const -> const math::mat4 &
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_projection;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto get_background_color() const -> const math::vec4 &
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_background_color;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void set_background_color(const math::vec4 &color)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							m_background_color = color;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						math::mat4 m_projection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						math::vec4 m_background_color = math::vec4(1.0f, 0.0f, 0.0f, 1.0f);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
							
								
								
									
										29
									
								
								modules/camera/public/component.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								modules/camera/public/component.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,29 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <camera/scene.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
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
					 | 
				
			||||||
							
								
								
									
										100
									
								
								modules/camera/public/scene.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								modules/camera/public/scene.hpp
									
										
									
									
									
										Normal 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
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
add_library_module(lt_debug instrumentor.cpp)
 | 
					add_library_module(lt_debug instrumentor.cpp)
 | 
				
			||||||
target_link_libraries(lt_debug PUBLIC logger)
 | 
					target_link_libraries(lt_debug PUBLIC logger)
 | 
				
			||||||
target_precompile_headers(lt_debug PUBLIC
 | 
					target_precompile_headers(lt_debug PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/private/pch.hpp)
 | 
				
			||||||
                          ${CMAKE_CURRENT_SOURCE_DIR}/private/pch.hpp)
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ void Instrumentor::end_session_impl()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (m_current_session_count == 0u)
 | 
						if (m_current_session_count == 0u)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		log::warn("0 profiling for the ended session");
 | 
							log_wrn("0 profiling for the ended session");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m_current_session_count = 0u;
 | 
						m_current_session_count = 0u;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <format>
 | 
					#include <format>
 | 
				
			||||||
 | 
					#include <logger/logger.hpp>
 | 
				
			||||||
#include <source_location>
 | 
					#include <source_location>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace lt {
 | 
					namespace lt {
 | 
				
			||||||
| 
						 | 
					@ -9,7 +10,7 @@ template<typename Expression_T, typename... Args_T>
 | 
				
			||||||
struct ensure
 | 
					struct ensure
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ensure(
 | 
						ensure(
 | 
				
			||||||
	    const Expression_T &expression,
 | 
						    Expression_T expression,
 | 
				
			||||||
	    std::format_string<Args_T...> fmt,
 | 
						    std::format_string<Args_T...> fmt,
 | 
				
			||||||
	    Args_T &&...args,
 | 
						    Args_T &&...args,
 | 
				
			||||||
	    const std::source_location &location = std::source_location::current()
 | 
						    const std::source_location &location = std::source_location::current()
 | 
				
			||||||
| 
						 | 
					@ -27,6 +28,7 @@ struct ensure
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template<typename Expression_T, typename... Args_T>
 | 
					template<typename Expression_T, typename... Args_T>
 | 
				
			||||||
ensure(Expression_T, std::format_string<Args_T...>, Args_T &&...)
 | 
					ensure(Expression_T, std::format_string<Args_T...>, Args_T &&...)
 | 
				
			||||||
    -> ensure<Expression_T, Args_T...>;
 | 
					    -> ensure<Expression_T, Args_T...>;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
add_library_module(ecs sparse_set.cpp)
 | 
					add_library_module(ecs entity.cpp scene.cpp uuid.cpp )
 | 
				
			||||||
target_link_libraries(ecs PUBLIC logger lt_debug memory)
 | 
					target_link_libraries(ecs 
 | 
				
			||||||
 | 
					    PUBLIC logger lt_debug EnTT::EnTT camera math
 | 
				
			||||||
add_test_module(ecs sparse_set.test.cpp registry.test.cpp)
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								modules/ecs/private/entity.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								modules/ecs/private/entity.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					#include <ecs/entity.hpp>
 | 
				
			||||||
 | 
					#include <ecs/scene.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Entity::Entity(entt::entity handle, Scene *scene): m_handle(handle), m_scene(scene)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
| 
						 | 
					@ -1,351 +0,0 @@
 | 
				
			||||||
#include <ecs/registry.hpp>
 | 
					 | 
				
			||||||
#include <ranges>
 | 
					 | 
				
			||||||
#include <test/expects.hpp>
 | 
					 | 
				
			||||||
#include <test/test.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using lt::test::Case;
 | 
					 | 
				
			||||||
using lt::test::expect_unreachable;
 | 
					 | 
				
			||||||
using lt::test::Suite;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using lt::test::expect_eq;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using lt::test::expect_false;
 | 
					 | 
				
			||||||
using lt::test::expect_true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using lt::ecs::EntityId;
 | 
					 | 
				
			||||||
using lt::ecs::Registry;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct Component
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int m_int {};
 | 
					 | 
				
			||||||
	std::string m_string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] friend auto operator==(const Component &lhs, const Component &rhs) -> bool
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return lhs.m_int == rhs.m_int && lhs.m_string == rhs.m_string;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
template<>
 | 
					 | 
				
			||||||
struct std::formatter<Component>
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	constexpr auto parse(std::format_parse_context &context)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return context.begin();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto format(const Component &val, std::format_context &context) const
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return std::format_to(context.out(), "{}, {}", val.m_int, val.m_string);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct Component_B
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	float m_float {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] friend auto operator==(const Component_B lhs, const Component_B &rhs) -> bool
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return lhs.m_float == rhs.m_float;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
template<>
 | 
					 | 
				
			||||||
struct std::formatter<Component_B>
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	constexpr auto parse(std::format_parse_context &context)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return context.begin();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto format(const Component_B &val, std::format_context &context) const
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return std::format_to(context.out(), "{}", val.m_float);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Suite raii = "raii"_suite = [] {
 | 
					 | 
				
			||||||
	Case { "happy path won't throw" } = [] {
 | 
					 | 
				
			||||||
		std::ignore = Registry {};
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "many won't freeze/throw" } = [] {
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 100'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			std::ignore = Registry {};
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "unhappy path throws" } = [] {
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "post construct has correct state" } = [] {
 | 
					 | 
				
			||||||
		auto registry = Registry {};
 | 
					 | 
				
			||||||
		expect_eq(registry.get_entity_count(), 0);
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Suite entity_raii = "entity_raii"_suite = [] {
 | 
					 | 
				
			||||||
	Case { "create_entity returns unique values" } = [] {
 | 
					 | 
				
			||||||
		auto registry = Registry {};
 | 
					 | 
				
			||||||
		auto set = std::unordered_set<EntityId> {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			auto entity = registry.create_entity();
 | 
					 | 
				
			||||||
			expect_false(set.contains(entity));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			set.insert(entity);
 | 
					 | 
				
			||||||
			expect_eq(set.size(), idx + 1);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "post create/destroy_entity has correct state" } = [] {
 | 
					 | 
				
			||||||
		auto registry = Registry {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		auto entities = std::vector<EntityId> {};
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			entities.emplace_back(registry.create_entity());
 | 
					 | 
				
			||||||
			expect_eq(registry.get_entity_count(), idx + 1);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			auto entity = entities.back();
 | 
					 | 
				
			||||||
			registry.destroy_entity(entity);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			entities.pop_back();
 | 
					 | 
				
			||||||
			expect_eq(registry.get_entity_count(), 10'000 - (idx + 1));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Suite component_raii = "component_raii"_suite = [] {
 | 
					 | 
				
			||||||
	Case { "add has correct state" } = [] {
 | 
					 | 
				
			||||||
		auto registry = Registry {};
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 100'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			auto entity = registry.create_entity();
 | 
					 | 
				
			||||||
			auto &component = registry.add<Component>(
 | 
					 | 
				
			||||||
			    entity,
 | 
					 | 
				
			||||||
			    { .m_int = idx, .m_string = std::to_string(idx) }
 | 
					 | 
				
			||||||
			);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			expect_eq(component.m_int, idx);
 | 
					 | 
				
			||||||
			expect_eq(component.m_string, std::to_string(idx));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "remove has correct state" } = [] {
 | 
					 | 
				
			||||||
		auto registry = Registry {};
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 100'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			auto entity = registry.create_entity();
 | 
					 | 
				
			||||||
			auto &component = registry.add<Component>(
 | 
					 | 
				
			||||||
			    entity,
 | 
					 | 
				
			||||||
			    { .m_int = idx, .m_string = std::to_string(idx) }
 | 
					 | 
				
			||||||
			);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			expect_eq(component.m_int, idx);
 | 
					 | 
				
			||||||
			expect_eq(component.m_string, std::to_string(idx));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Suite callbacks = "callbacks"_suite = [] {
 | 
					 | 
				
			||||||
	Case { "connecting on_construct/on_destruct won't throw" } = [] {
 | 
					 | 
				
			||||||
		auto registry = Registry {};
 | 
					 | 
				
			||||||
		registry.connect_on_construct<Component>([&](Registry &, EntityId) {});
 | 
					 | 
				
			||||||
		registry.connect_on_destruct<Component>([&](Registry &, EntityId) {});
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "on_construct/on_destruct won't get called on unrelated component" } = [] {
 | 
					 | 
				
			||||||
		auto registry = Registry {};
 | 
					 | 
				
			||||||
		registry.connect_on_construct<Component>([&](Registry &, EntityId) {
 | 
					 | 
				
			||||||
			expect_unreachable();
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
		registry.connect_on_destruct<Component>([&](Registry &, EntityId) {
 | 
					 | 
				
			||||||
			expect_unreachable();
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 100'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			registry.add<Component_B>(registry.create_entity(), {});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "on_construct/on_destruct gets called" } = [] {
 | 
					 | 
				
			||||||
		auto registry = Registry {};
 | 
					 | 
				
			||||||
		auto all_entities = std::vector<EntityId> {};
 | 
					 | 
				
			||||||
		auto on_construct_called = std::vector<EntityId> {};
 | 
					 | 
				
			||||||
		auto on_destruct_called = std::vector<EntityId> {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		registry.connect_on_construct<Component>([&](Registry &, EntityId entity) {
 | 
					 | 
				
			||||||
			on_construct_called.emplace_back(entity);
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
		registry.connect_on_destruct<Component>([&](Registry &, EntityId entity) {
 | 
					 | 
				
			||||||
			on_destruct_called.emplace_back(entity);
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		expect_true(on_construct_called.empty());
 | 
					 | 
				
			||||||
		expect_true(on_destruct_called.empty());
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 100'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			auto entity = all_entities.emplace_back(registry.create_entity());
 | 
					 | 
				
			||||||
			registry.add<Component>(entity, {});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		expect_eq(on_construct_called, all_entities);
 | 
					 | 
				
			||||||
		expect_true(on_destruct_called.empty());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (auto &entity : all_entities)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			registry.remove<Component>(entity);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		expect_eq(on_construct_called, all_entities);
 | 
					 | 
				
			||||||
		expect_eq(on_destruct_called, all_entities);
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Suite each = "each"_suite = [] {
 | 
					 | 
				
			||||||
	auto registry = Registry {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto shared_entity_counter = 0u;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto component_map_a = std::unordered_map<EntityId, Component> {};
 | 
					 | 
				
			||||||
	auto entities_a = std::vector<EntityId> {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		auto entity = entities_a.emplace_back(registry.create_entity());
 | 
					 | 
				
			||||||
		auto &component = registry.add<Component>(
 | 
					 | 
				
			||||||
		    entity,
 | 
					 | 
				
			||||||
		    { .m_int = idx, .m_string = std::to_string(idx) }
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		component_map_a[entity] = component;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto component_map_b = std::unordered_map<lt::ecs::EntityId, Component_B> {};
 | 
					 | 
				
			||||||
	for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		auto entity = EntityId {};
 | 
					 | 
				
			||||||
		if (idx % 3 == 0)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			entity = entities_a[idx];
 | 
					 | 
				
			||||||
			++shared_entity_counter;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			entity = registry.create_entity();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		auto &component = registry.add<Component_B>(
 | 
					 | 
				
			||||||
		    entity,
 | 
					 | 
				
			||||||
		    { .m_float = static_cast<float>(idx) / 2.0f }
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		component_map_b[entity] = component;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "each one element" } = [&] {
 | 
					 | 
				
			||||||
		auto counter = 0u;
 | 
					 | 
				
			||||||
		registry.each<Component>([&](EntityId entity, Component &component) {
 | 
					 | 
				
			||||||
			++counter;
 | 
					 | 
				
			||||||
			expect_eq(component_map_a[entity], component);
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		expect_eq(component_map_a.size(), counter);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		counter = 0u;
 | 
					 | 
				
			||||||
		registry.each<Component_B>([&](EntityId entity, Component_B &component) {
 | 
					 | 
				
			||||||
			++counter;
 | 
					 | 
				
			||||||
			expect_eq(component_map_b[entity], component);
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
		expect_eq(component_map_b.size(), counter);
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "each two element" } = [&] {
 | 
					 | 
				
			||||||
		auto counter = 0u;
 | 
					 | 
				
			||||||
		registry.each<Component, Component_B>(
 | 
					 | 
				
			||||||
		    [&](EntityId entity, Component &component_a, Component_B &component_b) {
 | 
					 | 
				
			||||||
			    expect_eq(component_map_a[entity], component_a);
 | 
					 | 
				
			||||||
			    expect_eq(component_map_b[entity], component_b);
 | 
					 | 
				
			||||||
			    ++counter;
 | 
					 | 
				
			||||||
		    }
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		expect_eq(counter, shared_entity_counter);
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Suite views = "views"_suite = [] {
 | 
					 | 
				
			||||||
	auto registry = Registry {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto shared_entity_counter = 0u;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto component_map_a = std::unordered_map<EntityId, Component> {};
 | 
					 | 
				
			||||||
	auto entities_a = std::vector<EntityId> {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		auto entity = entities_a.emplace_back(registry.create_entity());
 | 
					 | 
				
			||||||
		auto &component = registry.add<Component>(
 | 
					 | 
				
			||||||
		    entity,
 | 
					 | 
				
			||||||
		    { .m_int = idx, .m_string = std::to_string(idx) }
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		component_map_a[entity] = component;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto component_map_b = std::unordered_map<EntityId, Component_B> {};
 | 
					 | 
				
			||||||
	for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		auto entity = EntityId {};
 | 
					 | 
				
			||||||
		if (idx % 3 == 0)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			entity = entities_a[idx];
 | 
					 | 
				
			||||||
			++shared_entity_counter;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			entity = registry.create_entity();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		auto &component = registry.add<Component_B>(
 | 
					 | 
				
			||||||
		    entity,
 | 
					 | 
				
			||||||
		    { .m_float = static_cast<float>(idx) / 2.0f }
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		component_map_b[entity] = component;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "view one component" } = [&] {
 | 
					 | 
				
			||||||
		for (const auto &[entity, component] : registry.view<Component>())
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			expect_eq(component_map_a[entity], component);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (const auto &[entity, component] : registry.view<Component_B>())
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			expect_eq(component_map_b[entity], component);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "view two component" } = [&] {
 | 
					 | 
				
			||||||
		auto counter = 0u;
 | 
					 | 
				
			||||||
		for (const auto &[entity, component, component_b] : registry.view<Component, Component_B>())
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			expect_eq(component_map_a[entity], component);
 | 
					 | 
				
			||||||
			expect_eq(component_map_b[entity], component_b);
 | 
					 | 
				
			||||||
			++counter;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		expect_eq(counter, shared_entity_counter);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		counter = 0u;
 | 
					 | 
				
			||||||
		for (const auto &[entity, component_b, component] : registry.view<Component_B, Component>())
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			expect_eq(component_map_b[entity], component_b);
 | 
					 | 
				
			||||||
			expect_eq(component_map_a[entity], component);
 | 
					 | 
				
			||||||
			++counter;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		expect_eq(counter, shared_entity_counter);
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
							
								
								
									
										31
									
								
								modules/ecs/private/scene.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								modules/ecs/private/scene.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,31 @@
 | 
				
			||||||
 | 
					#include <ecs/components.hpp>
 | 
				
			||||||
 | 
					#include <ecs/entity.hpp>
 | 
				
			||||||
 | 
					#include <ecs/scene.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto Scene::create_entity(const std::string &name, const TransformComponent &transform) -> Entity
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return create_entity_with_uuid(name, UUID(), transform);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto Scene::get_entity_by_tag(const std::string &tag) -> Entity
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return {};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto Scene::create_entity_with_uuid(
 | 
				
			||||||
 | 
					    const std::string &name,
 | 
				
			||||||
 | 
					    UUID uuid,
 | 
				
			||||||
 | 
					    const TransformComponent &transform
 | 
				
			||||||
 | 
					) -> Entity
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						auto entity = Entity { m_registry.create(), this };
 | 
				
			||||||
 | 
						entity.add_component<TagComponent>(name);
 | 
				
			||||||
 | 
						entity.add_component<TransformComponent>(transform);
 | 
				
			||||||
 | 
						entity.add_component<UUIDComponent>(uuid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return entity;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
							
								
								
									
										325
									
								
								modules/ecs/private/serializer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										325
									
								
								modules/ecs/private/serializer.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,325 @@
 | 
				
			||||||
 | 
					#include <asset_manager/asset_manager.hpp>
 | 
				
			||||||
 | 
					#include <camera/component.hpp>
 | 
				
			||||||
 | 
					#include <ecs/components.hpp>
 | 
				
			||||||
 | 
					#include <ecs/serializer.hpp>
 | 
				
			||||||
 | 
					#include <math/vec3.hpp>
 | 
				
			||||||
 | 
					#include <math/vec4.hpp>
 | 
				
			||||||
 | 
					#include <yaml-cpp/yaml.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace YAML {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<>
 | 
				
			||||||
 | 
					struct convert<lt::math::vec3>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static auto encode(const lt::math::vec3 &rhs) -> Node
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto node = Node {};
 | 
				
			||||||
 | 
							node.push_back(rhs.x);
 | 
				
			||||||
 | 
							node.push_back(rhs.y);
 | 
				
			||||||
 | 
							node.push_back(rhs.z);
 | 
				
			||||||
 | 
							return node;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static auto decode(const Node &node, lt::math::vec3 &rhs) -> bool
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (!node.IsSequence() || node.size() != 3)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rhs.x = node[0].as<float>();
 | 
				
			||||||
 | 
							rhs.y = node[1].as<float>();
 | 
				
			||||||
 | 
							rhs.z = node[2].as<float>();
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<>
 | 
				
			||||||
 | 
					struct convert<lt::math::vec4>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static auto encode(const lt::math::vec4 &rhs) -> Node
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto node = Node {};
 | 
				
			||||||
 | 
							node.push_back(rhs.x);
 | 
				
			||||||
 | 
							node.push_back(rhs.y);
 | 
				
			||||||
 | 
							node.push_back(rhs.z);
 | 
				
			||||||
 | 
							node.push_back(rhs.w);
 | 
				
			||||||
 | 
							return node;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static auto decode(const Node &node, lt::math::vec4 &rhs) -> bool
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (!node.IsSequence() || node.size() != 4)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rhs.x = node[0].as<float>();
 | 
				
			||||||
 | 
							rhs.y = node[1].as<float>();
 | 
				
			||||||
 | 
							rhs.z = node[2].as<float>();
 | 
				
			||||||
 | 
							rhs.w = node[3].as<float>();
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					} // namespace YAML
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto operator<<(YAML::Emitter &out, const math::vec3 &v) -> YAML::Emitter &
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						out << YAML::Flow;
 | 
				
			||||||
 | 
						out << YAML::BeginSeq << v.x << v.y << v.z << YAML::EndSeq;
 | 
				
			||||||
 | 
						return out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto operator<<(YAML::Emitter &out, const math::vec4 &v) -> YAML::Emitter &
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						out << YAML::Flow;
 | 
				
			||||||
 | 
						out << YAML::BeginSeq << v.x << v.y << v.z << v.w << YAML::EndSeq;
 | 
				
			||||||
 | 
						return out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SceneSerializer::SceneSerializer(const Ref<Scene> &scene): m_scene(scene)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SceneSerializer::serialize(const std::string &filePath)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						auto out = YAML::Emitter {};
 | 
				
			||||||
 | 
						out << YAML::BeginMap; // Scene
 | 
				
			||||||
 | 
						out << YAML::Key << "Scene" << YAML::Value << "Untitled";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						out << YAML::Key << "Entities" << YAML::Value << YAML::BeginSeq;
 | 
				
			||||||
 | 
						for (auto [entityID, storage] : m_scene->m_registry.storage())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto entity = Entity { static_cast<entt::entity>(entityID), m_scene.get() };
 | 
				
			||||||
 | 
							if (!entity.is_valid())
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							serialize_entity(out, entity);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						out << YAML::EndSeq;
 | 
				
			||||||
 | 
						out << YAML::EndMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::filesystem::create_directories(filePath.substr(0ull, filePath.find_last_of('\\')));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto fout = std::ofstream { filePath };
 | 
				
			||||||
 | 
						if (!fout.is_open())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							log_trc("Failed to create fout at: {}", filePath);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fout << out.c_str();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto SceneSerializer::deserialize(const std::string &file_path) -> bool
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						auto stream = std::ifstream { file_path };
 | 
				
			||||||
 | 
						auto ss = std::stringstream {};
 | 
				
			||||||
 | 
						ss << stream.rdbuf();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto data = YAML::Load(ss.str());
 | 
				
			||||||
 | 
						if (!data["Scene"])
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto sceneName = data["Scene"].as<std::string>();
 | 
				
			||||||
 | 
						log_trc("Deserializing scene: '{}'", sceneName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto entities = data["Entities"];
 | 
				
			||||||
 | 
						if (entities)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto texturePaths = std::unordered_set<std::string> {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (auto entity : entities)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								auto uuid = entity["entity"].as<uint64_t>(); // #todo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								auto name = std::string {};
 | 
				
			||||||
 | 
								auto tagComponent = entity["TagComponent"];
 | 
				
			||||||
 | 
								if (tagComponent)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									name = tagComponent["Tag"].as<std::string>();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								log_trc("Deserialized entity '{}' : '{}'", uuid, name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								auto deserializedEntity = m_scene->create_entity_with_uuid(name, uuid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								auto gg = deserializedEntity.get_component<TagComponent>();
 | 
				
			||||||
 | 
								log_trc("tag: {}", gg.tag);
 | 
				
			||||||
 | 
								auto transformComponent = entity["TransformComponent"];
 | 
				
			||||||
 | 
								if (transformComponent)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									auto &entityTransforomComponent = deserializedEntity
 | 
				
			||||||
 | 
									                                      .get_component<TransformComponent>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									entityTransforomComponent.translation = transformComponent["Translation"]
 | 
				
			||||||
 | 
									                                            .as<math::vec3>();
 | 
				
			||||||
 | 
									entityTransforomComponent.rotation = transformComponent["Rotation"]
 | 
				
			||||||
 | 
									                                         .as<math::vec3>();
 | 
				
			||||||
 | 
									entityTransforomComponent.scale = transformComponent["Scale"].as<math::vec3>();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* #TEMPORARY SOLUTION# */
 | 
				
			||||||
 | 
								auto spriteRendererComponent = entity["SpriteRendererComponent"];
 | 
				
			||||||
 | 
								if (spriteRendererComponent)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									auto &entitySpriteRendererComponent = deserializedEntity
 | 
				
			||||||
 | 
									                                          .add_component<SpriteRendererComponent>();
 | 
				
			||||||
 | 
									entitySpriteRendererComponent.tint = spriteRendererComponent["Tint"]
 | 
				
			||||||
 | 
									                                         .as<math::vec4>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									auto texturePath = spriteRendererComponent["Texture"].as<std::string>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (!texturePaths.contains(texturePath))
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										AssetManager::load_texture(texturePath, texturePath);
 | 
				
			||||||
 | 
										texturePaths.insert(texturePath);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									entitySpriteRendererComponent.texture = AssetManager::get_texture(texturePath);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								/* #TEMPORARY SOLUTION# */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								auto cameraComponent = entity["CameraComponent"];
 | 
				
			||||||
 | 
								if (cameraComponent)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									auto &entityCameraComponent = deserializedEntity.add_component<CameraComponent>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									const auto &cameraSpecifications = cameraComponent["Camera"];
 | 
				
			||||||
 | 
									entityCameraComponent.camera.set_projection_type(
 | 
				
			||||||
 | 
									    (SceneCamera::ProjectionType)cameraSpecifications["ProjectionType"].as<int>()
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									entityCameraComponent.camera.set_orthographic_size(
 | 
				
			||||||
 | 
									    cameraSpecifications["OrthographicSize"].as<float>()
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									entityCameraComponent.camera.set_orthographic_near_plane(
 | 
				
			||||||
 | 
									    cameraSpecifications["OrthographicNearPlane"].as<float>()
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									entityCameraComponent.camera.set_orthographic_far_plane(
 | 
				
			||||||
 | 
									    cameraSpecifications["OrthographicFarPlane"].as<float>()
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									entityCameraComponent.camera.set_perspective_vertical_fov(
 | 
				
			||||||
 | 
									    cameraSpecifications["PerspectiveVerticalFOV"].as<float>()
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									entityCameraComponent.camera.set_perspective_near_plane(
 | 
				
			||||||
 | 
									    cameraSpecifications["PerspectiveNearPlane"].as<float>()
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									entityCameraComponent.camera.set_perspective_far_plane(
 | 
				
			||||||
 | 
									    cameraSpecifications["PerspectiveFarPlane"].as<float>()
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									entityCameraComponent.camera.set_background_color(
 | 
				
			||||||
 | 
									    cameraSpecifications["BackgroundColor"].as<math::vec4>()
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									entityCameraComponent.isPrimary = cameraComponent["IsPrimary"].as<bool>();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SceneSerializer::serialize_binary(const std::string & /*filePath*/)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						log_err("NO_IMPLEMENT");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto SceneSerializer::deserialize_binary(const std::string & /*filePath*/) -> bool
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						log_err("NO_IMPLEMENT");
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SceneSerializer::serialize_entity(YAML::Emitter &out, Entity entity)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						out << YAML::BeginMap;                                            // entity
 | 
				
			||||||
 | 
						out << YAML::Key << "entity" << YAML::Value << entity.get_uuid(); // dummy uuid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (entity.has_component<TagComponent>())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							out << YAML::Key << "TagComponent";
 | 
				
			||||||
 | 
							out << YAML::BeginMap; // tag component
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto &tagComponent = entity.get_component<TagComponent>().tag;
 | 
				
			||||||
 | 
							out << YAML::Key << "Tag" << YAML::Value << tagComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							out << YAML::EndMap; // tag component
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (entity.has_component<TransformComponent>())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							out << YAML::Key << "TransformComponent";
 | 
				
			||||||
 | 
							out << YAML::BeginMap; // transform component
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto &transformComponent = entity.get_component<TransformComponent>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							out << YAML::Key << "Translation" << YAML::Value << transformComponent.translation;
 | 
				
			||||||
 | 
							out << YAML::Key << "Rotation" << YAML::Value << transformComponent.rotation;
 | 
				
			||||||
 | 
							out << YAML::Key << "Scale" << YAML::Value << transformComponent.scale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							out << YAML::EndMap; // transform component;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (entity.has_component<SpriteRendererComponent>())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// TODO(Light): get scene serialization/de-serialization working.
 | 
				
			||||||
 | 
							// out << YAML::Key << "SpriteRendererComponent";
 | 
				
			||||||
 | 
							// out << YAML::BeginMap; // sprite renderer component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// auto &spriteRendererComponent = entity.get_component<SpriteRendererComponent>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// out << YAML::Key << "Texture" << YAML::Value
 | 
				
			||||||
 | 
							//     << spriteRendererComponent.texture->GetFilePath();
 | 
				
			||||||
 | 
							// out << YAML::Key << "Tint" << YAML::Value << spriteRendererComponent.tint;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// out << YAML::EndMap; // sprite renderer component
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// #todo:
 | 
				
			||||||
 | 
						// if(entity.has_component<NativeScriptComponent>())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (entity.has_component<CameraComponent>())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							out << YAML::Key << "CameraComponent";
 | 
				
			||||||
 | 
							out << YAML::BeginMap; // camera component
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto &cameraComponent = entity.get_component<CameraComponent>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							out << YAML::Key << "Camera" << YAML::Value;
 | 
				
			||||||
 | 
							out << YAML::BeginMap; // camera
 | 
				
			||||||
 | 
							out << YAML::Key << "OrthographicSize" << YAML::Value
 | 
				
			||||||
 | 
							    << cameraComponent.camera.get_orthographic_size();
 | 
				
			||||||
 | 
							out << YAML::Key << "OrthographicFarPlane" << YAML::Value
 | 
				
			||||||
 | 
							    << cameraComponent.camera.get_orthographic_far_plane();
 | 
				
			||||||
 | 
							out << YAML::Key << "OrthographicNearPlane" << YAML::Value
 | 
				
			||||||
 | 
							    << cameraComponent.camera.get_orthographic_near_plane();
 | 
				
			||||||
 | 
							out << YAML::Key << "PerspectiveVerticalFOV" << YAML::Value
 | 
				
			||||||
 | 
							    << cameraComponent.camera.get_perspective_vertical_fov();
 | 
				
			||||||
 | 
							out << YAML::Key << "PerspectiveFarPlane" << YAML::Value
 | 
				
			||||||
 | 
							    << cameraComponent.camera.get_perspective_far_plane();
 | 
				
			||||||
 | 
							out << YAML::Key << "PerspectiveNearPlane" << YAML::Value
 | 
				
			||||||
 | 
							    << cameraComponent.camera.get_perspective_near_plane();
 | 
				
			||||||
 | 
							out << YAML::Key << "ProjectionType" << YAML::Value
 | 
				
			||||||
 | 
							    << (int)cameraComponent.camera.get_projection_type();
 | 
				
			||||||
 | 
							out << YAML::Key << "BackgroundColor" << YAML::Value
 | 
				
			||||||
 | 
							    << cameraComponent.camera.get_background_color();
 | 
				
			||||||
 | 
							out << YAML::EndMap; // camera
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							out << YAML::Key << "IsPrimary" << YAML::Value << cameraComponent.isPrimary;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							out << YAML::EndMap; // camera component
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						out << YAML::EndMap; // entity
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
| 
						 | 
					@ -1,163 +0,0 @@
 | 
				
			||||||
#include <ecs/sparse_set.hpp>
 | 
					 | 
				
			||||||
#include <ranges>
 | 
					 | 
				
			||||||
#include <test/expects.hpp>
 | 
					 | 
				
			||||||
#include <test/test.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using lt::test::Case;
 | 
					 | 
				
			||||||
using lt::test::Suite;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using lt::test::expect_eq;
 | 
					 | 
				
			||||||
using lt::test::expect_false;
 | 
					 | 
				
			||||||
using lt::test::expect_ne;
 | 
					 | 
				
			||||||
using lt::test::expect_throw;
 | 
					 | 
				
			||||||
using lt::test::expect_true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using Set = lt::ecs::SparseSet<int>;
 | 
					 | 
				
			||||||
constexpr auto capacity = 100;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Suite raii = "raii"_suite = [] {
 | 
					 | 
				
			||||||
	Case { "happy path won't throw" } = [] {
 | 
					 | 
				
			||||||
		std::ignore = Set {};
 | 
					 | 
				
			||||||
		std::ignore = Set { Set::max_capacity };
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "unhappy path throws" } = [] {
 | 
					 | 
				
			||||||
		expect_throw([] { std::ignore = Set { Set::max_capacity + 1 }; });
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "post construct has correct state" } = [&] {
 | 
					 | 
				
			||||||
		auto set = Set { capacity };
 | 
					 | 
				
			||||||
		expect_eq(set.get_size(), 0);
 | 
					 | 
				
			||||||
		expect_eq(set.get_capacity(), capacity);
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Suite element_raii = "element_raii"_suite = [] {
 | 
					 | 
				
			||||||
	Case { "many inserts/removes won't freeze/throw" } = [] {
 | 
					 | 
				
			||||||
		auto set = Set {};
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			set.insert(idx, {});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			set.remove(idx);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "insert returns reference to inserted value" } = [] {
 | 
					 | 
				
			||||||
		auto set = Set {};
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			const auto val = Set::Dense_T { idx, {} };
 | 
					 | 
				
			||||||
			expect_eq(set.insert(val.first, val.second), val);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "post insert/remove has correct state" } = [] {
 | 
					 | 
				
			||||||
		auto set = Set {};
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			set.insert(idx, idx * 2);
 | 
					 | 
				
			||||||
			expect_eq(set.get_size(), idx + 1);
 | 
					 | 
				
			||||||
			expect_eq(set.at(idx), Set::Dense_T { idx, idx * 2 });
 | 
					 | 
				
			||||||
			expect_true(set.contains(idx));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			expect_eq(set.at(idx), Set::Dense_T { idx, idx * 2 });
 | 
					 | 
				
			||||||
			expect_true(set.contains(idx));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			set.remove(idx);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			expect_eq(set.get_size(), 10'000 - (idx + 1));
 | 
					 | 
				
			||||||
			expect_false(set.contains(idx));
 | 
					 | 
				
			||||||
			expect_throw([&] { std::ignore = set.at(idx); });
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "removed elements won't be iterated again" } = [] {
 | 
					 | 
				
			||||||
		auto set = Set {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			set.insert(idx, idx);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		set.remove(0);
 | 
					 | 
				
			||||||
		set.remove(32);
 | 
					 | 
				
			||||||
		set.remove(69);
 | 
					 | 
				
			||||||
		set.remove(420);
 | 
					 | 
				
			||||||
		set.remove(9'999);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (auto &[identifier, value] : set)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			expect_eq(identifier, value);
 | 
					 | 
				
			||||||
			expect_ne(value, 0);
 | 
					 | 
				
			||||||
			expect_ne(value, 32);
 | 
					 | 
				
			||||||
			expect_ne(value, 69);
 | 
					 | 
				
			||||||
			expect_ne(value, 420);
 | 
					 | 
				
			||||||
			expect_ne(value, 9'999);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Suite getters = "getters"_suite = [] {
 | 
					 | 
				
			||||||
	Case { "get_size returns correct values" } = [] {
 | 
					 | 
				
			||||||
		auto set = Set {};
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			expect_eq(set.get_size(), idx);
 | 
					 | 
				
			||||||
			set.insert(idx, {});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		expect_eq(set.get_size(), 10'000);
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "get_capacity returns correct values" } = [] {
 | 
					 | 
				
			||||||
		auto set = Set { 10'000 };
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			expect_eq(set.get_capacity(), 10'000); // are we testing std::vector's implementation?
 | 
					 | 
				
			||||||
			set.insert(idx, {});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		expect_eq(set.get_capacity(), 10'000);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		set.insert(set.get_size(), {});
 | 
					 | 
				
			||||||
		expect_ne(set.get_capacity(), 10'000);
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Case { "at throws with out of bound access" } = [] {
 | 
					 | 
				
			||||||
		auto set = Set {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 50))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			expect_throw([&] {
 | 
					 | 
				
			||||||
				set.insert(idx, {});
 | 
					 | 
				
			||||||
				std::ignore = set.at(50);
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		set.insert(50, {});
 | 
					 | 
				
			||||||
		std::ignore = set.at(50); // should not throw
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Suite clear = "clear"_suite = [] {
 | 
					 | 
				
			||||||
	Case { "post clear has correct state" } = [] {
 | 
					 | 
				
			||||||
		auto set = Set { 0 };
 | 
					 | 
				
			||||||
		for (auto idx : std::views::iota(0, 10'000))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			set.insert(idx, {});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		set.clear();
 | 
					 | 
				
			||||||
		expect_eq(set.get_size(), 0);
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
							
								
								
									
										14
									
								
								modules/ecs/private/uuid.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								modules/ecs/private/uuid.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					#include <ecs/uuid.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::mt19937_64 UUID::s_engine = std::mt19937_64(std::random_device()());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::uniform_int_distribution<uint64_t>
 | 
				
			||||||
 | 
					    UUID::s_distribution = std::uniform_int_distribution<uint64_t> {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					UUID::UUID(uint64_t uuid /* = -1 */): m_uuid(uuid == -1 ? s_distribution(s_engine) : uuid)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
							
								
								
									
										6
									
								
								modules/ecs/public/components.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								modules/ecs/public/components.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <ecs/components/native_script.hpp>
 | 
				
			||||||
 | 
					#include <ecs/components/sprite_renderer.hpp>
 | 
				
			||||||
 | 
					#include <ecs/components/tag.hpp>
 | 
				
			||||||
 | 
					#include <ecs/components/transform.hpp>
 | 
				
			||||||
							
								
								
									
										28
									
								
								modules/ecs/public/components/native_script.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								modules/ecs/public/components/native_script.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <ecs/components/scriptable_entity.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct NativeScriptComponent
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						NativeScript *(*CreateInstance)();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void (*DestroyInstance)(NativeScriptComponent *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						template<typename t>
 | 
				
			||||||
 | 
						void bind()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							CreateInstance = []() {
 | 
				
			||||||
 | 
								return static_cast<NativeScript *>(new t());
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							DestroyInstance = [](NativeScriptComponent *nsc) {
 | 
				
			||||||
 | 
								delete (t *)(nsc->instance);
 | 
				
			||||||
 | 
								nsc->instance = nullptr;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						NativeScript *instance;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
							
								
								
									
										46
									
								
								modules/ecs/public/components/scriptable_entity.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								modules/ecs/public/components/scriptable_entity.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,46 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <ecs/entity.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NativeScript
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						friend class Scene;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						NativeScript() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtual ~NativeScript() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto get_uid() const -> unsigned int
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_unique_identifier;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						template<typename t>
 | 
				
			||||||
 | 
						auto GetComponent() -> t &
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_entity.get_component<t>();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						virtual void on_create()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtual void on_destroy()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtual void on_update(float ts)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						Entity m_entity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned int m_unique_identifier = 0; // :#todo
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
							
								
								
									
										35
									
								
								modules/ecs/public/components/sprite_renderer.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								modules/ecs/public/components/sprite_renderer.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <math/vec4.hpp>
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Texture;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SpriteRendererComponent
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						SpriteRendererComponent() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SpriteRendererComponent(const SpriteRendererComponent &) = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SpriteRendererComponent(
 | 
				
			||||||
 | 
						    Ref<Texture> _texture,
 | 
				
			||||||
 | 
						    const math::vec4 &_tint = math::vec4 { 1.0f, 1.0f, 1.0f, 1.0f }
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						    : texture(std::move(std::move(_texture)))
 | 
				
			||||||
 | 
						    , tint(_tint)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						operator Ref<Texture>() const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return texture;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Ref<Texture> texture;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						math::vec4 tint {};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
							
								
								
									
										30
									
								
								modules/ecs/public/components/tag.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								modules/ecs/public/components/tag.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct TagComponent
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						TagComponent() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TagComponent(const TagComponent &) = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TagComponent(std::string _tag): tag(std::move(_tag))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						operator std::string() const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return tag;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						operator const std::string &() const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return tag;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::string tag = "Unnamed";
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
							
								
								
									
										43
									
								
								modules/ecs/public/components/transform.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								modules/ecs/public/components/transform.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <math/mat4.hpp>
 | 
				
			||||||
 | 
					#include <math/vec3.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct TransformComponent
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						TransformComponent(const TransformComponent &) = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TransformComponent(
 | 
				
			||||||
 | 
						    const math::vec3 &_translation = math::vec3(0.0f, 0.0f, 0.0f),
 | 
				
			||||||
 | 
						    const math::vec3 &_scale = math::vec3(1.0f, 1.0f, 1.0f),
 | 
				
			||||||
 | 
						    const math::vec3 &_rotation = math::vec3(0.0f, 0.0f, 0.0f)
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    : translation(_translation)
 | 
				
			||||||
 | 
						    , scale(_scale)
 | 
				
			||||||
 | 
						    , rotation(_rotation)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] auto get_transform() const -> math::mat4
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return math::translate(translation)
 | 
				
			||||||
 | 
							       * math::rotate(rotation.z, math::vec3 { 0.0f, 0.0f, 1.0f }) //
 | 
				
			||||||
 | 
							       * math::scale(scale);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						operator const math::mat4() const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return get_transform();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						math::vec3 translation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						math::vec3 scale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						math::vec3 rotation;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
							
								
								
									
										18
									
								
								modules/ecs/public/components/uuid.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								modules/ecs/public/components/uuid.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <ecs/uuid.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct UUIDComponent
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						UUIDComponent(UUID _uuid): uuid(_uuid)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UUIDComponent(const UUIDComponent &) = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UUID uuid;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
| 
						 | 
					@ -1,54 +1,59 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <ecs/registry.hpp>
 | 
					#include <ecs/components/uuid.hpp>
 | 
				
			||||||
#include <memory/reference.hpp>
 | 
					#include <ecs/scene.hpp>
 | 
				
			||||||
 | 
					#include <entt/entt.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace lt::ecs {
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** High-level entity convenience wrapper */
 | 
					 | 
				
			||||||
class Entity
 | 
					class Entity
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	Entity(memory::Ref<Registry> registry, EntityId identifier)
 | 
						Entity(entt::entity handle = entt::null, Scene *scene = nullptr);
 | 
				
			||||||
	    : m_registry(std::move(registry))
 | 
					
 | 
				
			||||||
	    , m_identifier(identifier)
 | 
						template<typename t, typename... Args>
 | 
				
			||||||
 | 
						auto add_component(Args &&...args) -> t &
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		ensure(m_registry, "Failed to create Entity ({}): null registry", m_identifier);
 | 
							return m_scene->m_registry.emplace<t>(m_handle, std::forward<Args>(args)...);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename Component_T>
 | 
						template<typename t>
 | 
				
			||||||
	auto add(Component_T component) -> Component_T &
 | 
						auto get_component() -> t &
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return m_registry->add(m_identifier, component);
 | 
							return m_scene->m_registry.get<t>(m_handle);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename Component_T>
 | 
						template<typename t>
 | 
				
			||||||
	auto get() -> Component_T &
 | 
						auto has_component() -> bool
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return m_registry->get<Component_T>(m_identifier);
 | 
							return m_scene->m_registry.any_of<t>(m_handle);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template<typename Component_T>
 | 
						template<typename t>
 | 
				
			||||||
	auto get() const -> const Component_T &
 | 
						void remove_component()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return m_registry->get<Component_T>(m_identifier);
 | 
							m_scene->m_registry.remove<t>(m_handle);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto get_registry() -> memory::Ref<Registry>
 | 
						auto get_uuid() -> uint64_t
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return m_registry;
 | 
							return get_component<UUIDComponent>().uuid;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] auto id() const -> EntityId
 | 
						[[nodiscard]] auto is_valid() const -> bool
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return m_identifier;
 | 
							return m_handle != entt::null && m_scene != nullptr;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						operator uint32_t()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return (uint32_t)m_handle;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	memory::Ref<Registry> m_registry;
 | 
						entt::entity m_handle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EntityId m_identifier;
 | 
						Scene *m_scene;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
} // namespace lt::ecs
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,260 +0,0 @@
 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <ecs/sparse_set.hpp>
 | 
					 | 
				
			||||||
#include <memory/scope.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace lt::ecs {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using EntityId = uint32_t;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
constexpr auto null_entity = std::numeric_limits<EntityId>::max();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** A registry of components, the heart of an ECS architecture.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @todo(Light): Implement grouping
 | 
					 | 
				
			||||||
 * @todo(Light): Implement identifier recycling
 | 
					 | 
				
			||||||
 * @todo(Light): Optimize views/each
 | 
					 | 
				
			||||||
 * @todo(Light): Support >2 component views
 | 
					 | 
				
			||||||
 * @todo(Light): Handle more edge cases or specify the undefined behaviors
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @ref https://skypjack.github.io/
 | 
					 | 
				
			||||||
 * @ref https://github.com/alecthomas/entityx
 | 
					 | 
				
			||||||
 * @ref https://github.com/skypjack/entt
 | 
					 | 
				
			||||||
 * @ref https://github.com/SanderMertens/flecs
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class Registry
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	using UnderlyingSparseSet_T = TypeErasedSparseSet<EntityId>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	using Callback_T = std::function<void(Registry &, EntityId)>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename Component_T>
 | 
					 | 
				
			||||||
	void connect_on_construct(Callback_T callback)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		m_on_construct_hooks[get_type_id<Component_T>()] = callback;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename Component_T>
 | 
					 | 
				
			||||||
	void connect_on_destruct(Callback_T callback)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		m_on_destruct_hooks[get_type_id<Component_T>()] = callback;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename Component_T>
 | 
					 | 
				
			||||||
	void disconnect_on_construct()
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		m_on_construct_hooks.erase(get_type_id<Component_T>());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename Component_T>
 | 
					 | 
				
			||||||
	void disconnect_on_destruct()
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		m_on_destruct_hooks.erase(get_type_id<Component_T>());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto create_entity() -> EntityId
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		++m_entity_count;
 | 
					 | 
				
			||||||
		return m_current++;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void destroy_entity(EntityId entity)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		for (const auto &[key, set] : m_sparsed_sets)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			set->remove(entity);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		--m_entity_count;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename Component_T>
 | 
					 | 
				
			||||||
	auto get(EntityId entity) const -> const Component_T &
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		auto &derived_set = get_derived_set<Component_T>();
 | 
					 | 
				
			||||||
		return derived_set.at(entity).second;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename Component_T>
 | 
					 | 
				
			||||||
	auto get(EntityId entity) -> Component_T &
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		auto &derived_set = get_derived_set<Component_T>();
 | 
					 | 
				
			||||||
		return derived_set.at(entity).second;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename Component_T>
 | 
					 | 
				
			||||||
	auto add(EntityId entity, Component_T component) -> Component_T &
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		auto &derived_set = get_derived_set<Component_T>();
 | 
					 | 
				
			||||||
		auto &added_component = derived_set.insert(entity, std::move(component)).second;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (m_on_construct_hooks.contains(get_type_id<Component_T>()))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			m_on_construct_hooks[get_type_id<Component_T>()](*this, entity);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return added_component;
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename Component_T>
 | 
					 | 
				
			||||||
	void remove(EntityId entity)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if (m_on_destruct_hooks.contains(get_type_id<Component_T>()))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			m_on_destruct_hooks[get_type_id<Component_T>()](*this, entity);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		auto &derived_set = get_derived_set<Component_T>();
 | 
					 | 
				
			||||||
		derived_set.remove(entity);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename Component_T>
 | 
					 | 
				
			||||||
	auto view() -> SparseSet<Component_T, EntityId> &
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return get_derived_set<Component_T>();
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename ComponentA_T, typename ComponentB_T>
 | 
					 | 
				
			||||||
	    requires(!std::is_same_v<ComponentA_T, ComponentB_T>)
 | 
					 | 
				
			||||||
	auto view() -> std::vector<std::tuple<EntityId, ComponentA_T &, ComponentB_T &>>
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		auto &set_a = get_derived_set<ComponentA_T>();
 | 
					 | 
				
			||||||
		auto &set_b = get_derived_set<ComponentB_T>();
 | 
					 | 
				
			||||||
		auto view = std::vector<std::tuple<EntityId, ComponentA_T &, ComponentB_T &>> {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* iterate over the "smaller" component-set, and check if its entities have the other
 | 
					 | 
				
			||||||
		 * component */
 | 
					 | 
				
			||||||
		if (set_a.get_size() > set_b.get_size())
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			for (auto &[entity, component_b] : set_b)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				if (set_a.contains(entity))
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					view.emplace_back(std::tie(entity, set_a.at(entity).second, component_b));
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			for (auto &[entity, component_a] : set_a)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				if (set_b.contains(entity))
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					view.emplace_back(std::tie(entity, component_a, set_b.at(entity).second));
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return view;
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename Component_T>
 | 
					 | 
				
			||||||
	void each(std::function<void(EntityId, Component_T &)> functor)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		for (auto &[entity, component] : get_derived_set<Component_T>())
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			functor(entity, component);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename ComponentA_T, typename ComponentB_T>
 | 
					 | 
				
			||||||
	    requires(!std::is_same_v<ComponentA_T, ComponentB_T>)
 | 
					 | 
				
			||||||
	void each(std::function<void(EntityId, ComponentA_T &, ComponentB_T &)> functor)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		auto &set_a = get_derived_set<ComponentA_T>();
 | 
					 | 
				
			||||||
		auto &set_b = get_derived_set<ComponentB_T>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* iterate over the "smaller" component-set, and check if its entities have the other
 | 
					 | 
				
			||||||
		 * component */
 | 
					 | 
				
			||||||
		if (set_a.get_size() > set_b.get_size())
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			for (auto &[entity, component_b] : set_b)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				if (set_a.contains(entity))
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					functor(entity, set_a.at(entity).second, component_b);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (auto &[entity, component_a] : set_a)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if (set_b.contains(entity))
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				functor(entity, component_a, set_b.at(entity).second);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] auto get_entity_count() const -> size_t
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return static_cast<size_t>(m_entity_count);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	using TypeId = size_t;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	static consteval auto hash_cstr(const char *str) -> TypeId
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		constexpr auto fnv_offset_basis = size_t { 14695981039346656037ull };
 | 
					 | 
				
			||||||
		constexpr auto fnv_prime = size_t { 1099511628211ull };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		auto hash = fnv_offset_basis;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (const auto &ch : std::string_view { str })
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			hash *= fnv_prime;
 | 
					 | 
				
			||||||
			hash ^= static_cast<uint8_t>(ch);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return hash;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename T>
 | 
					 | 
				
			||||||
	static consteval auto get_type_id() -> TypeId
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
#if defined _MSC_VER
 | 
					 | 
				
			||||||
	#define GENERATOR_PRETTY_FUNCTION __FUNCSIG__
 | 
					 | 
				
			||||||
#elif defined __clang__ || (defined __GNUC__)
 | 
					 | 
				
			||||||
	#define GENERATOR_PRETTY_FUNCTION __PRETTY_FUNCTION__
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
	#error "Compiler not supported"
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
		constexpr auto value = hash_cstr(GENERATOR_PRETTY_FUNCTION);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#undef GENERATOR_PRETTY_FUNCTION
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return value;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	template<typename T>
 | 
					 | 
				
			||||||
	auto get_derived_set() -> SparseSet<T, EntityId> &
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		constexpr auto type_id = get_type_id<T>();
 | 
					 | 
				
			||||||
		if (!m_sparsed_sets.contains(type_id))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			m_sparsed_sets[type_id] = memory::create_scope<SparseSet<T, EntityId>>();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		auto *base_set = m_sparsed_sets[type_id].get();
 | 
					 | 
				
			||||||
		auto *derived_set = dynamic_cast<SparseSet<T, EntityId> *>(base_set);
 | 
					 | 
				
			||||||
		ensure(derived_set, "Failed to downcast to derived set");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return *derived_set;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	EntityId m_current;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TypeId m_entity_count;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::flat_map<TypeId, memory::Scope<UnderlyingSparseSet_T>> m_sparsed_sets;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::flat_map<TypeId, Callback_T> m_on_construct_hooks;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::flat_map<TypeId, Callback_T> m_on_destruct_hooks;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace lt::ecs
 | 
					 | 
				
			||||||
							
								
								
									
										65
									
								
								modules/ecs/public/scene.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								modules/ecs/public/scene.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,65 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <ecs/components/transform.hpp>
 | 
				
			||||||
 | 
					#include <ecs/uuid.hpp>
 | 
				
			||||||
 | 
					#include <entt/entt.hpp>
 | 
				
			||||||
 | 
					#include <functional>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Entity;
 | 
				
			||||||
 | 
					class Framebuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Scene
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						template<typename... T>
 | 
				
			||||||
 | 
						auto group()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_registry.group(entt::get<T...>);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						template<typename T>
 | 
				
			||||||
 | 
						auto view()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_registry.view<T>();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto create_entity(
 | 
				
			||||||
 | 
						    const std::string &name,
 | 
				
			||||||
 | 
						    const TransformComponent &transform = TransformComponent()
 | 
				
			||||||
 | 
						) -> Entity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto get_entity_by_tag(const std::string &tag) -> Entity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto get_entt_registry() -> entt::registry &
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_registry;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						friend class Entity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						friend class SceneSerializer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						friend class SceneHierarchyPanel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entt::registry m_registry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto create_entity_with_uuid(
 | 
				
			||||||
 | 
						    const std::string &name,
 | 
				
			||||||
 | 
						    UUID uuid,
 | 
				
			||||||
 | 
						    const TransformComponent &transform = TransformComponent()
 | 
				
			||||||
 | 
						) -> Entity;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ecs {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using Registry = Scene;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using Entity = ::lt::Entity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace ecs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
							
								
								
									
										34
									
								
								modules/ecs/public/serializer.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								modules/ecs/public/serializer.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <ecs/entity.hpp>
 | 
				
			||||||
 | 
					#include <ecs/scene.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace YAML {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Emitter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SceneSerializer
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						SceneSerializer(const Ref<Scene> &scene);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void serialize(const std::string &filePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto deserialize(const std::string &file_path) -> bool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void serialize_binary(const std::string &file_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto deserialize_binary(const std::string &file_path) -> bool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						Ref<Scene> m_scene;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void serialize_entity(YAML::Emitter &out, Entity entity);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
| 
						 | 
					@ -1,173 +0,0 @@
 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace lt::ecs {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @ref https://programmingpraxis.com/2012/03/09/sparse-sets/
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
template<typename Identifier_T = uint32_t>
 | 
					 | 
				
			||||||
class TypeErasedSparseSet
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	TypeErasedSparseSet() = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TypeErasedSparseSet(TypeErasedSparseSet &&) = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TypeErasedSparseSet(const TypeErasedSparseSet &) = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto operator=(TypeErasedSparseSet &&) -> TypeErasedSparseSet & = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto operator=(const TypeErasedSparseSet &) -> TypeErasedSparseSet & = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	virtual ~TypeErasedSparseSet() = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	virtual void remove(Identifier_T identifier) = 0;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template<typename Value_T, typename Identifier_T = uint32_t>
 | 
					 | 
				
			||||||
class SparseSet: public TypeErasedSparseSet<Identifier_T>
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	using Dense_T = std::pair<Identifier_T, Value_T>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	static constexpr auto max_capacity = size_t { 1'000'000 };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	static constexpr auto null_identifier = std::numeric_limits<Identifier_T>().max();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	explicit SparseSet(size_t initial_capacity = 1)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		ensure(
 | 
					 | 
				
			||||||
		    initial_capacity <= max_capacity,
 | 
					 | 
				
			||||||
		    "Failed to create SparseSet: capacity too large ({} > {})",
 | 
					 | 
				
			||||||
		    initial_capacity,
 | 
					 | 
				
			||||||
		    max_capacity
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		m_dense.reserve(initial_capacity);
 | 
					 | 
				
			||||||
		m_sparse.resize(initial_capacity, null_identifier);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto insert(Identifier_T identifier, Value_T value) -> Dense_T &
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if (m_sparse.size() < identifier + 1)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			auto new_capacity = std::max(static_cast<size_t>(identifier + 1), m_sparse.size() * 2);
 | 
					 | 
				
			||||||
			new_capacity = std::min(new_capacity, max_capacity);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// log::debug("Increasing sparse vector size:", m_dead_count);
 | 
					 | 
				
			||||||
			// log::debug("\tdead_count: {}", m_dead_count);
 | 
					 | 
				
			||||||
			// log::debug("\talive_count: {}", m_alive_count);
 | 
					 | 
				
			||||||
			// log::debug("\tsparse.size: {} -> {}", m_sparse.size(), new_capacity);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			m_sparse.resize(new_capacity, null_identifier);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		++m_alive_count;
 | 
					 | 
				
			||||||
		m_sparse[identifier] = m_dense.size();
 | 
					 | 
				
			||||||
		return m_dense.emplace_back(identifier, std::move(value));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** @warn invalidates begin/end iterators
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @todo(Light): make it not invalidate the iterators >:c
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	void remove(Identifier_T identifier) override
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		auto &idx = m_sparse[identifier];
 | 
					 | 
				
			||||||
		auto &[entity, component] = m_dense[idx];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		auto &[last_entity, last_component] = m_dense.back();
 | 
					 | 
				
			||||||
		auto &last_idx = m_sparse[last_entity];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// removed entity is in dense's back, just pop and invalidate sparse[identifier]
 | 
					 | 
				
			||||||
		if (entity == last_entity)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			idx = null_identifier;
 | 
					 | 
				
			||||||
			m_dense.pop_back();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			// swap dense's 'back' to 'removed'
 | 
					 | 
				
			||||||
			std::swap(component, last_component);
 | 
					 | 
				
			||||||
			entity = last_entity;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// make sparse point to new idx
 | 
					 | 
				
			||||||
			last_idx = idx;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// pop dense and invalidate sparse[identifier]
 | 
					 | 
				
			||||||
			idx = null_identifier;
 | 
					 | 
				
			||||||
			m_dense.pop_back();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		++m_dead_count;
 | 
					 | 
				
			||||||
		--m_alive_count;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void clear()
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		m_dense.clear();
 | 
					 | 
				
			||||||
		m_sparse.clear();
 | 
					 | 
				
			||||||
		m_alive_count = 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] auto contains(Identifier_T identifier) const -> bool
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return m_sparse.size() > identifier               //
 | 
					 | 
				
			||||||
		       && m_sparse[identifier] != null_identifier //
 | 
					 | 
				
			||||||
		       && m_dense[m_sparse[identifier]].first == identifier;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto begin() -> std::vector<Dense_T>::iterator
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return m_dense.begin();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto end() -> std::vector<Dense_T>::iterator
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return m_dense.end();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] auto at(Identifier_T identifier) const -> const Dense_T &
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return m_dense.at(m_sparse.at(identifier));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] auto at(Identifier_T identifier) -> Dense_T &
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return m_dense.at(m_sparse.at(identifier));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** @warn unsafe, for bound-checked access: use `.at` */
 | 
					 | 
				
			||||||
	[[nodiscard]] auto &&operator[](this auto &&self, Identifier_T identifier)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		using Self_T = decltype(self);
 | 
					 | 
				
			||||||
		return std::forward<Self_T>(self).m_dense[std::forward<Self_T>(self).m_sparse[identifier]];
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] auto get_size() const noexcept -> size_t
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return m_alive_count;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] auto get_capacity() const noexcept -> size_t
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return m_sparse.capacity();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] auto is_empty() const noexcept -> bool
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return m_dense.empty();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	std::vector<Dense_T> m_dense;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::vector<Identifier_T> m_sparse;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	size_t m_alive_count {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	size_t m_dead_count {};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace lt::ecs
 | 
					 | 
				
			||||||
							
								
								
									
										38
									
								
								modules/ecs/public/uuid.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								modules/ecs/public/uuid.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,38 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <random>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace lt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UUID
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						UUID(uint64_t uuid = -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						operator uint64_t() const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return m_uuid;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						static std::mt19937_64 s_engine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static std::uniform_int_distribution<uint64_t> s_distribution;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint64_t m_uuid;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace lt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace std {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<>
 | 
				
			||||||
 | 
					struct hash<lt::UUID>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::size_t operator()(const lt::UUID &uuid) const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return hash<uint64_t>()(static_cast<uint64_t>(uuid));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace std
 | 
				
			||||||
							
								
								
									
										1
									
								
								modules/env/CMakeLists.txt
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								modules/env/CMakeLists.txt
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
add_library_module(env)
 | 
					 | 
				
			||||||
							
								
								
									
										68
									
								
								modules/env/public/constants.hpp
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										68
									
								
								modules/env/public/constants.hpp
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,68 +0,0 @@
 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace lt {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum class Platform : uint8_t
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/** The GNU/Linux platform.
 | 
					 | 
				
			||||||
	 * Tested on the following distros: arch-x86_64
 | 
					 | 
				
			||||||
	 * @note: Named like so because `linux` is a built-in identifier.
 | 
					 | 
				
			||||||
	 * */
 | 
					 | 
				
			||||||
	gnu_linux,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The Microsoft Windows(tm) platform.
 | 
					 | 
				
			||||||
	 * Tested on the following architectures: x86_64
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	windows,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The apple's macOS platform.
 | 
					 | 
				
			||||||
	 * Currently not supported.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	mac,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** The compiler that was used for compiling the project. */
 | 
					 | 
				
			||||||
enum class Compiler : uint8_t
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	clang,
 | 
					 | 
				
			||||||
	gcc,
 | 
					 | 
				
			||||||
	msvc,
 | 
					 | 
				
			||||||
	apple_clang,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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)
 | 
					 | 
				
			||||||
constexpr auto platform = Platform::gnu_linux;
 | 
					 | 
				
			||||||
constexpr auto platform_name = "gnu_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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef __clang__
 | 
					 | 
				
			||||||
constexpr auto compiler = Compiler::clang;
 | 
					 | 
				
			||||||
constexpr auto compiler_name = "clang";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** @todo(Light): insert the full identifier, including version information and such */
 | 
					 | 
				
			||||||
constexpr auto full_compiler_identifier = "clang";
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace constants
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace lt
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
add_library_module(input system.cpp)
 | 
					add_library_module(input system.cpp)
 | 
				
			||||||
target_link_libraries(input PUBLIC surface math logger tbb)
 | 
					target_link_libraries(input PUBLIC surface math  logger)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_test_module(input system.test.cpp)
 | 
					add_test_module(input system.test.cpp)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,5 @@
 | 
				
			||||||
#include <input/components.hpp>
 | 
					#include <input/components.hpp>
 | 
				
			||||||
#include <input/system.hpp>
 | 
					#include <input/system.hpp>
 | 
				
			||||||
#include <memory/reference.hpp>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace lt::input {
 | 
					namespace lt::input {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,23 +9,22 @@ struct overloads: Ts...
 | 
				
			||||||
	using Ts::operator()...;
 | 
						using Ts::operator()...;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
System::System(memory::Ref<ecs::Registry> registry): m_registry(std::move(registry))
 | 
					System::System(Ref<ecs::Registry> registry): m_registry(std::move(registry))
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ensure(m_registry, "Failed to initialize input system: null registry");
 | 
						ensure(m_registry, "Failed to initialize input system: null registry");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void System::tick(app::TickInfo tick)
 | 
					auto System::tick() -> bool
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	for (auto &[entity, surface] : m_registry->view<surface::SurfaceComponent>())
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						m_registry->view<surface::SurfaceComponent>().each([&](const entt::entity,
 | 
				
			||||||
 | 
						                                                       surface::SurfaceComponent &surface) {
 | 
				
			||||||
		for (const auto &event : surface.peek_events())
 | 
							for (const auto &event : surface.peek_events())
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			handle_event(event);
 | 
								handle_event(event);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (auto &[entity, input] : m_registry->view<InputComponent>())
 | 
						m_registry->view<InputComponent>().each([&](const entt::entity, InputComponent &input) {
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		// TODO(Light): instead of iterating over all actions each frame,
 | 
							// TODO(Light): instead of iterating over all actions each frame,
 | 
				
			||||||
		// make a list of "dirty" actions to reset
 | 
							// make a list of "dirty" actions to reset
 | 
				
			||||||
		// and a surface_input->input_action mapping to get to action through input
 | 
							// and a surface_input->input_action mapping to get to action through input
 | 
				
			||||||
| 
						 | 
					@ -50,14 +48,9 @@ void System::tick(app::TickInfo tick)
 | 
				
			||||||
				action.state = InputAction::State::inactive;
 | 
									action.state = InputAction::State::inactive;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const auto now = std::chrono::steady_clock::now();
 | 
						return false;
 | 
				
			||||||
	m_last_tick_result = app::TickResult {
 | 
					 | 
				
			||||||
		.info = tick,
 | 
					 | 
				
			||||||
		.duration = now - tick.start_time,
 | 
					 | 
				
			||||||
		.end_time = now,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void System::on_register()
 | 
					void System::on_register()
 | 
				
			||||||
| 
						 | 
					@ -101,7 +94,7 @@ void System::on_key_press(const lt::surface::KeyPressedEvent &event)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (event.get_key() > m_keys.size())
 | 
						if (event.get_key() > m_keys.size())
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		log::debug(
 | 
							log_dbg(
 | 
				
			||||||
		    "Key code larger than key container size, implement platform-dependant "
 | 
							    "Key code larger than key container size, implement platform-dependant "
 | 
				
			||||||
		    "key-code-mapping!"
 | 
							    "key-code-mapping!"
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
| 
						 | 
					@ -116,7 +109,7 @@ void System::on_key_release(const lt::surface::KeyReleasedEvent &event)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (event.get_key() > m_keys.size())
 | 
						if (event.get_key() > m_keys.size())
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		log::debug(
 | 
							log_dbg(
 | 
				
			||||||
		    "Key code larger than key container size, implement platform-dependant "
 | 
							    "Key code larger than key container size, implement platform-dependant "
 | 
				
			||||||
		    "key-code-mapping!"
 | 
							    "key-code-mapping!"
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,6 @@
 | 
				
			||||||
#include <ecs/entity.hpp>
 | 
					#include <ecs/entity.hpp>
 | 
				
			||||||
#include <input/components.hpp>
 | 
					#include <input/components.hpp>
 | 
				
			||||||
#include <input/system.hpp>
 | 
					#include <input/system.hpp>
 | 
				
			||||||
#include <memory/reference.hpp>
 | 
					 | 
				
			||||||
#include <memory/scope.hpp>
 | 
					 | 
				
			||||||
#include <ranges>
 | 
					 | 
				
			||||||
#include <surface/system.hpp>
 | 
					 | 
				
			||||||
#include <test/test.hpp>
 | 
					#include <test/test.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NOLINTBEGIN
 | 
					// NOLINTBEGIN
 | 
				
			||||||
| 
						 | 
					@ -21,49 +17,35 @@ using test::expect_throw;
 | 
				
			||||||
using test::Suite;
 | 
					using test::Suite;
 | 
				
			||||||
// NOLINTEND
 | 
					// NOLINTEND
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[nodiscard]] auto tick_info() -> app::TickInfo
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return {
 | 
					 | 
				
			||||||
		.delta_time = std::chrono::milliseconds { 16 },
 | 
					 | 
				
			||||||
		.budget = std::chrono::milliseconds { 10 },
 | 
					 | 
				
			||||||
		.start_time = std::chrono::steady_clock::now(),
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Fixture
 | 
					class Fixture
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	[[nodiscard]] auto registry() -> memory::Ref<ecs::Registry>
 | 
						[[nodiscard]] auto registry() -> Ref<ecs::Registry>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return m_registry;
 | 
							return m_registry;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto add_input_component() -> ecs::EntityId
 | 
						auto add_input_component() -> ecs::Entity
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		auto entity = m_registry->create_entity();
 | 
							auto entity = m_registry->create_entity("");
 | 
				
			||||||
		m_registry->add<InputComponent>(entity, {});
 | 
							entity.add_component<InputComponent>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return entity;
 | 
							return entity;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto add_surface_component() -> ecs::EntityId
 | 
						auto add_surface_component() -> ecs::Entity
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		auto entity = m_registry->create_entity();
 | 
							auto entity = m_registry->create_entity("");
 | 
				
			||||||
		m_surface_system.create_surface_component(
 | 
							entity.add_component<surface::SurfaceComponent>(surface::SurfaceComponent::CreateInfo {});
 | 
				
			||||||
		    entity,
 | 
					 | 
				
			||||||
		    { .title = "", .resolution = { 20u, 20u } }
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return entity;
 | 
							return entity;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	memory::Ref<ecs::Registry> m_registry = memory::create_ref<ecs::Registry>();
 | 
						Ref<ecs::Registry> m_registry = create_ref<ecs::Registry>();
 | 
				
			||||||
 | 
					 | 
				
			||||||
	surface::System m_surface_system = surface::System { m_registry };
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Suite raii = "raii"_suite = "raii"_suite = [] {
 | 
					Suite raii = [] {
 | 
				
			||||||
	Case { "happy path won't throw" } = [&] {
 | 
						Case { "happy path won't throw" } = [&] {
 | 
				
			||||||
		System { Fixture {}.registry() };
 | 
							System { Fixture {}.registry() };
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
| 
						 | 
					@ -81,76 +63,67 @@ Suite raii = "raii"_suite = "raii"_suite = [] {
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Suite system_events = "system_events"_suite = [] {
 | 
					Suite system_events = [] {
 | 
				
			||||||
	Case { "on_register won't throw" } = [] {
 | 
						Case { "on_register won't throw" } = [] {
 | 
				
			||||||
		auto fixture = Fixture {};
 | 
							auto fixture = Fixture {};
 | 
				
			||||||
		auto registry = fixture.registry();
 | 
							auto system = System { fixture.registry() };
 | 
				
			||||||
		auto system = System { registry };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		system.on_register();
 | 
							system.on_register();
 | 
				
			||||||
		expect_eq(registry->view<InputComponent>().get_size(), 0);
 | 
							expect_eq(fixture.registry()->view<InputComponent>().size(), 0);
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Case { "on_unregister won't throw" } = [] {
 | 
						Case { "on_unregister won't throw" } = [] {
 | 
				
			||||||
		auto fixture = Fixture {};
 | 
							auto fixture = Fixture {};
 | 
				
			||||||
		auto registry = fixture.registry();
 | 
							auto system = System { fixture.registry() };
 | 
				
			||||||
		auto system = System { registry };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		system.on_register();
 | 
							system.on_register();
 | 
				
			||||||
		system.on_unregister();
 | 
							system.on_unregister();
 | 
				
			||||||
		expect_eq(registry->view<InputComponent>().get_size(), 0);
 | 
							expect_eq(fixture.registry()->view<InputComponent>().size(), 0);
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Suite registry_events = "registry_events"_suite = [] {
 | 
					Suite registry_events = [] {
 | 
				
			||||||
	Case { "on_construct<InputComnent>" } = [] {
 | 
						Case { "on_construct<InputComnent>" } = [] {
 | 
				
			||||||
		auto fixture = Fixture {};
 | 
							auto fixture = Fixture {};
 | 
				
			||||||
		auto registry = fixture.registry();
 | 
							auto system = System { fixture.registry() };
 | 
				
			||||||
		auto system = System { registry };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const auto &entity = fixture.add_input_component();
 | 
							const auto &entity = fixture.add_input_component();
 | 
				
			||||||
		expect_eq(registry->view<InputComponent>().get_size(), 1);
 | 
							expect_eq(fixture.registry()->view<InputComponent>().size(), 1);
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Case { "on_destrroy<InputComponent>" } = [] {
 | 
						Case { "on_destrroy<InputComponent>" } = [] {
 | 
				
			||||||
		auto fixture = Fixture {};
 | 
							auto fixture = Fixture {};
 | 
				
			||||||
		auto registry = fixture.registry();
 | 
							auto system = create_scope<System>(fixture.registry());
 | 
				
			||||||
		auto system = memory::create_scope<System>(registry);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto entity_a = fixture.add_input_component();
 | 
							auto entity_a = fixture.add_input_component();
 | 
				
			||||||
		auto entity_b = fixture.add_input_component();
 | 
							auto entity_b = fixture.add_input_component();
 | 
				
			||||||
		expect_eq(registry->view<InputComponent>().get_size(), 2);
 | 
							expect_eq(fixture.registry()->view<InputComponent>().size(), 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		registry->remove<InputComponent>(entity_a);
 | 
							entity_a.remove_component<InputComponent>();
 | 
				
			||||||
		expect_eq(registry->view<InputComponent>().get_size(), 1);
 | 
							expect_eq(fixture.registry()->view<InputComponent>().size(), 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		system.reset();
 | 
							system.reset();
 | 
				
			||||||
		expect_eq(registry->view<InputComponent>().get_size(), 1);
 | 
							expect_eq(fixture.registry()->view<InputComponent>().size(), 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		registry->remove<InputComponent>(entity_b);
 | 
							entity_b.remove_component<InputComponent>();
 | 
				
			||||||
		expect_eq(registry->view<InputComponent>().get_size(), 0);
 | 
							expect_eq(fixture.registry()->view<InputComponent>().size(), 0);
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Suite tick = "tick"_suite = [] {
 | 
					Suite tick = [] {
 | 
				
			||||||
	Case { "Empty tick won't throw" } = [] {
 | 
						Case { "Empty tick won't throw" } = [] {
 | 
				
			||||||
		auto fixture = Fixture {};
 | 
							auto fixture = Fixture {};
 | 
				
			||||||
		auto registry = fixture.registry();
 | 
					 | 
				
			||||||
		auto system = System { fixture.registry() };
 | 
							auto system = System { fixture.registry() };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		system.tick(tick_info());
 | 
							expect_false(system.tick());
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Case { "Tick triggers input action" } = [] {
 | 
						Case { "Tick triggers input action" } = [] {
 | 
				
			||||||
		auto fixture = Fixture {};
 | 
							auto fixture = Fixture {};
 | 
				
			||||||
		auto registry = fixture.registry();
 | 
					 | 
				
			||||||
		auto system = System { fixture.registry() };
 | 
							auto system = System { fixture.registry() };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto surface_entity = fixture.add_surface_component();
 | 
							auto &surface = fixture.add_surface_component().get_component<surface::SurfaceComponent>();
 | 
				
			||||||
		auto &surface = registry->get<surface::SurfaceComponent>(surface_entity);
 | 
							auto &input = fixture.add_input_component().get_component<InputComponent>();
 | 
				
			||||||
 | 
					 | 
				
			||||||
		auto input_entity = fixture.add_input_component();
 | 
					 | 
				
			||||||
		auto &input = registry->get<InputComponent>(input_entity);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto action_key = input.add_action(
 | 
							auto action_key = input.add_action(
 | 
				
			||||||
		    {
 | 
							    {
 | 
				
			||||||
| 
						 | 
					@ -160,37 +133,32 @@ Suite tick = "tick"_suite = [] {
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
 | 
							expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
 | 
				
			||||||
		system.tick(tick_info());
 | 
							system.tick();
 | 
				
			||||||
		expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
 | 
							expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		surface.push_event(surface::KeyPressedEvent(69));
 | 
							surface.push_event(surface::KeyPressedEvent(69));
 | 
				
			||||||
		system.tick(tick_info());
 | 
							system.tick();
 | 
				
			||||||
		expect_eq(input.get_action(action_key).state, input::InputAction::State::triggered);
 | 
							expect_eq(input.get_action(action_key).state, input::InputAction::State::triggered);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		system.tick(tick_info());
 | 
							system.tick();
 | 
				
			||||||
		expect_eq(input.get_action(action_key).state, input::InputAction::State::active);
 | 
							expect_eq(input.get_action(action_key).state, input::InputAction::State::active);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		system.tick(tick_info());
 | 
							system.tick();
 | 
				
			||||||
		system.tick(tick_info());
 | 
							system.tick();
 | 
				
			||||||
		system.tick(tick_info());
 | 
							system.tick();
 | 
				
			||||||
		expect_eq(input.get_action(action_key).state, input::InputAction::State::active);
 | 
							expect_eq(input.get_action(action_key).state, input::InputAction::State::active);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		surface.push_event(surface::KeyReleasedEvent(69));
 | 
							surface.push_event(surface::KeyReleasedEvent(69));
 | 
				
			||||||
		system.tick(tick_info());
 | 
							system.tick();
 | 
				
			||||||
		expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
 | 
							expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive);
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Case { "Tick triggers" } = [] {
 | 
						Case { "Tick triggers" } = [] {
 | 
				
			||||||
		auto fixture = Fixture {};
 | 
							auto fixture = Fixture {};
 | 
				
			||||||
		auto registry = fixture.registry();
 | 
					 | 
				
			||||||
		auto system = System { fixture.registry() };
 | 
							auto system = System { fixture.registry() };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto surface_entity = fixture.add_surface_component();
 | 
							auto &surface = fixture.add_surface_component().get_component<surface::SurfaceComponent>();
 | 
				
			||||||
		auto &surface = registry->get<surface::SurfaceComponent>(surface_entity);
 | 
							auto &input = fixture.add_input_component().get_component<InputComponent>();
 | 
				
			||||||
 | 
					 | 
				
			||||||
		auto input_entity = fixture.add_input_component();
 | 
					 | 
				
			||||||
		auto &input = registry->get<InputComponent>(input_entity);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto action_key = input.add_action(
 | 
							auto action_key = input.add_action(
 | 
				
			||||||
		    {
 | 
							    {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,7 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <app/system.hpp>
 | 
					#include <app/system.hpp>
 | 
				
			||||||
#include <ecs/registry.hpp>
 | 
					#include <ecs/scene.hpp>
 | 
				
			||||||
#include <memory/reference.hpp>
 | 
					 | 
				
			||||||
#include <surface/components.hpp>
 | 
					#include <surface/components.hpp>
 | 
				
			||||||
#include <surface/events/keyboard.hpp>
 | 
					#include <surface/events/keyboard.hpp>
 | 
				
			||||||
#include <surface/events/mouse.hpp>
 | 
					#include <surface/events/mouse.hpp>
 | 
				
			||||||
| 
						 | 
					@ -12,19 +11,14 @@ namespace lt::input {
 | 
				
			||||||
class System: public app::ISystem
 | 
					class System: public app::ISystem
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	System(memory::Ref<ecs::Registry> registry);
 | 
						System(Ref<ecs::Registry> registry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void tick(app::TickInfo tick) override;
 | 
						auto tick() -> bool override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void on_register() override;
 | 
						void on_register() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void on_unregister() override;
 | 
						void on_unregister() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] auto get_last_tick_result() const -> const app::TickResult & override
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return m_last_tick_result;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	void handle_event(const surface::SurfaceComponent::Event &event);
 | 
						void handle_event(const surface::SurfaceComponent::Event &event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,15 +34,13 @@ private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void on_button_release(const lt::surface::ButtonReleasedEvent &event);
 | 
						void on_button_release(const lt::surface::ButtonReleasedEvent &event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memory::Ref<ecs::Registry> m_registry;
 | 
						Ref<ecs::Registry> m_registry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::array<bool, 512> m_keys {};
 | 
						std::array<bool, 512> m_keys {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::array<bool, 512> m_buttons {};
 | 
						std::array<bool, 512> m_buttons {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	math::vec2 m_pointer_position;
 | 
						math::vec2 m_pointer_position;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	app::TickResult m_last_tick_result {};
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		
		Reference in a new issue