Compare commits

...
57 Commits
Author SHA1 Message Date
Riyyi cc33b5d481 Render+Asset: Fix derived Renderer2D, fix Skybox uniform 2024-08-31 19:54:23 +02:00
Riyyi 67bb6a5671 Render: Fix release build via out-of-line destructors 2024-08-31 00:29:03 +02:00
Riyyi 3ff8838e01 Render: Fix memory leak, use after free :^) 2024-08-30 23:02:28 +02:00
Riyyi 3ae6ecae8a Meta: Enable ASan and UBSan, disable warnings in vendor directory
(address sanitizer, undefined behavior sanitizer)
2024-08-29 22:15:13 +02:00
Riyyi 6b6c588378 Component: Put JSON serialization functions in a separate file 2024-08-25 21:10:03 +02:00
Riyyi 9145f90c1d Meta: Update ruc library 2024-08-25 13:26:04 +02:00
Riyyi b9043dbb4b Render: Add and implement shader storage buffer (SSBO) 2024-08-24 20:14:06 +02:00
Riyyi b6e68eccec Doc: Add GLSL std430 memory layout documentation 2024-08-20 21:31:01 +02:00
Riyyi d16fade497 Everywhere: Work towards lighting support 2024-08-19 17:56:57 +02:00
Riyyi b8e4d9ef3f Render: Allow arbitrary data in uniformbuffer objects 2024-08-19 17:41:31 +02:00
Riyyi 03e8210165 Event+Asset+Keycodes: Add keybind for taking screenshots 2024-08-17 21:05:43 +02:00
Riyyi dcf2a85208 Asset+Component+Render: Render quads in the world as 3D objects 2024-08-11 22:06:55 +02:00
Riyyi b2ed951b1e Render: Add more value setter types for uniformbuffer objects 2024-08-11 22:06:55 +02:00
Riyyi d63dfe2f9f Render: Update getter to match naming convention 2024-08-10 22:05:21 +02:00
Riyyi faacdca424 App: Print average frame-time on exit 2024-08-10 21:58:50 +02:00
Riyyi b5f3cae5ba Render+Scene: Implement uniformbuffer objects 2024-08-10 21:58:50 +02:00
Riyyi 1d5f5a1ad8 Shader: Cache uniform location lookup 2024-08-09 23:10:39 +02:00
Riyyi 3cd2fab637 Render: Add support for int/double shader attributes 2024-08-07 21:52:13 +02:00
Riyyi c5ed219ad2 Render: Dont create double framebuffers 2024-08-05 19:26:38 +02:00
Riyyi 4dbac7ee38 Render: Make Framebuffer class more flexible 2024-08-05 19:24:38 +02:00
Riyyi a5b7a49447 Render: Make Framebuffer class more flexible 2024-08-05 19:12:39 +02:00
Riyyi fd72f6610e Render: Change RenderCommand::clearBit function 2024-08-05 15:19:16 +02:00
Riyyi 21319b93ad Render: Cleanup buffer 2024-08-05 11:24:52 +02:00
Riyyi cd36841039 Asset+Render+App: Implement render to framebuffer 2024-08-04 20:54:57 +02:00
Riyyi 538d0b5ce7 Asset+Render: Add Framebuffers 2024-08-03 22:41:23 +02:00
Riyyi 5f6a5f48dd Asset+Component+Render: Fix assimp modeling loading + batching 2024-08-02 02:08:12 +02:00
Riyyi c06b06ed62 Scene+System: Enable model loading 2024-05-13 14:20:42 +02:00
Riyyi 5721b4249c Asset: Add 3D shader 2024-05-13 14:15:37 +02:00
Riyyi 95a64be4cd Renderer: Implement 3D 2024-05-13 14:14:36 +02:00
Riyyi 79b4f1c3d1 Asset: Implement assimp texture loading 2024-05-13 14:05:21 +02:00
Riyyi a266a27f88 Asset+Component: Add model component and asset 2024-05-13 14:05:05 +02:00
Riyyi 08969e32d1 Meta: Add assimp dependency 2024-05-13 13:56:48 +02:00
Riyyi 2793956be4 Render+Asset: De-duplicate different manager classes into one for assets 2024-04-29 13:36:49 +02:00
Riyyi 51c5874318 Add license file for glad 2024-04-20 16:51:32 +02:00
Riyyi d01a4429ca Renderer: Rename Character -> Font/Symbol 2024-01-20 00:17:15 +01:00
Riyyi d22effdfa3 Renderer: De-duplicate code between renderer types 2024-01-20 00:15:24 +01:00
Riyyi 52611beeab Renderer: Move RenderCommand to separate file 2024-01-14 20:24:17 +01:00
Riyyi fd8973d10d Engine: Implement skybox 2024-01-07 16:37:42 +01:00
Riyyi bc3f2c9db5 Component+Scene+System: Implement parent-child transform components 2024-01-05 20:50:49 +01:00
Riyyi 0dd7a05d46 Component+Scene: Restructure scene .json to allow multiple components 2024-01-01 13:10:20 +01:00
Riyyi c607ebcc72 Engine: Do not use offset for character advance 2023-12-29 22:53:29 +01:00
Riyyi 15b71f8387 Engine: Allow configuration of font size and line spacing 2023-12-29 22:12:13 +01:00
Riyyi 6e5f82913e Script: Add missing macro CONCAT 2023-12-26 22:06:02 +01:00
Riyyi d02d5a528b Assets: Add text entity to example scene1.json 2023-12-26 13:07:56 +01:00
Riyyi 574f2a8b5a Engine: Fix font spacing in TextAreaComponent 2023-12-26 01:31:12 +01:00
Riyyi 2b0635ed69 Meta: Assets in separate CMakeLists.txt 2023-12-18 21:50:10 +01:00
Riyyi 649d196577 Script+System: Allow native scripts from projects, decoupled from engine 2023-12-03 20:05:21 +01:00
Riyyi f47babe5cf Engine: Add ability to set camera zoom-level via scene JSON 2022-10-16 12:44:35 +02:00
Riyyi efae5b9adb Engine: Add Scene::findEntity() function 2022-10-16 11:39:19 +02:00
Riyyi a1b02c2e3a Meta: Update ruc library 2022-10-15 11:33:19 +02:00
Riyyi e926d3ce4a CMake: Link against ruc-test library 2022-09-28 14:36:44 +02:00
Riyyi 4c479cfb64 Meta: Update ruc library 2022-09-28 14:34:17 +02:00
Riyyi cf0a20e208 Engine: Add scene getter 2022-09-28 14:20:28 +02:00
Riyyi 55af11d831 Engine: Add constructor for raw data texture 2022-09-28 14:15:52 +02:00
Riyyi 26cd35d679 CMake: Populate list of required include directories for library targets
PUBLIC items will populate the INTERFACE_INCLUDE_DIRECTORIES property of
<target>. This property is a list of public include directories
requirements for a library.

Targets may populate this property to publish the include directories
required to compile against the headers for the target.
2022-09-28 14:05:02 +02:00
Riyyi 6972128ad2 Assets: Remove font from test scene, as font assets aren't added yet 2022-09-27 13:12:13 +02:00
Riyyi c858ef3b1d CMake: Always copy assets to build directory, fixes example: $ make run 2022-09-27 13:10:33 +02:00
115 changed files with 5888 additions and 2276 deletions
+4 -1
View File
@@ -16,6 +16,9 @@
[submodule "inferno/vendor/stb/stb"]
path = vendor/stb/stb
url = https://github.com/nothings/stb
[submodule "vendor/ruc"]
[submodule "inferno/vendor/ruc"]
path = vendor/ruc
url = https://github.com/Riyyi/ruc
[submodule "inferno/vendor/assimp"]
path = vendor/assimp
url = https://github.com/assimp/assimp
+23 -13
View File
@@ -11,6 +11,7 @@ endif()
# Options
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
option(INFERNO_BUILD_EXAMPLES "Build the Inferno example programs" ${INFERNO_STANDALONE})
option(INFERNO_BUILD_WARNINGS "Build with warnings enabled" ${INFERNO_STANDALONE})
# ------------------------------------------
@@ -25,10 +26,15 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Compiler flags used for all build types
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
# -Wall = All warnings about contructions that are easily avoidable
# -Wextra = Extra warning flags not covered by -Wall
# -Wpedantic = Warnings for compiler extensions not part of the standard
set(COMPILE_FLAGS_DEPS -w)
if(INFERNO_BUILD_WARNINGS)
set(COMPILE_FLAGS_PROJECT -Wall -Wextra -Wpedantic)
# -Wall = All warnings about contructions that are easily avoidable
# -Wextra = Extra warning flags not covered by -Wall
# -Wpedantic = Warnings for compiler extensions not part of the standard
else()
set(COMPILE_FLAGS_PROJECT ${COMPILE_FLAGS_DEPS})
endif()
# Set default build type if not specified
set(DEFAULT_BUILD_TYPE Release)
@@ -42,14 +48,20 @@ endif()
# Set build type specific compiler flags
message("--- ${CMAKE_BUILD_TYPE} ---")
if(${CMAKE_BUILD_TYPE} STREQUAL Debug)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -g -pg")
# -Og = Optimizations that do not interfere with debugging
# -g = Produce debugging information in OS's native format
# -pg = Generate profile information for analysis with gprof
# Optimizations that do not interfere with debugging
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -Og")
# Produce debugging information in OS's native format
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -g")
# Generate profile information for analysis with gprof
# $ gprof <PROJECT> gmon.out > profile-data.txt
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -pg")
# Enable ASan (Address Sanitizer) and UBSan (Undefined Behavior Sanitizer)
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fsanitize=address,undefined")
# Do not omit frame pointer, which helps with debugging.
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit-frame-pointer")
elseif(${CMAKE_BUILD_TYPE} STREQUAL Release)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
# -O3 = Optimizations that increase compilation time and performance
# Optimizations that increase compilation time and performance
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -O3")
endif()
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
@@ -69,9 +81,7 @@ add_subdirectory("src")
# ------------------------------------------
# Assets target
add_custom_target(${ENGINE}-assets ALL
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/copy-assets.cmake
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_subdirectory("assets")
# ------------------------------------------
# Examples target
+3
View File
@@ -33,6 +33,7 @@ $ cmake .. && make
* Libraries
- [[https://github.com/assimp/assimp][assimp]]
# - [[https://github.com/bulletphysics/bullet3][Bullet]]
- [[https://github.com/skypjack/entt][EnTT]]
- [[https://github.com/Dav1dde/glad][glad]]
@@ -48,3 +49,5 @@ $ cmake .. && make
- [[https://www.glfw.org/docs/latest/build_guide.html#build_link_cmake_source][Build GLFW using CMake]]
- [[https://learnopengl.com][Learn OpenGL]]
- [[https://www.youtube.com/playlist?list=PLlrATfBNZ98dC-V-N3m0Go4deliWHPFwT][Game Engine]] by The Cherno
- [[https://www.youtube.com/watch?v=mnIQEQoHHCU][OpenGL 3D Game Tutorial 32: Font Rendering]]
- [[https://youtu.be/d8cfgcJR9Tk][OpenGL 3D Game Tutorial 33: Distance Field Text Rendering]]
+4
View File
@@ -0,0 +1,4 @@
add_custom_target(${ENGINE}-assets
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/copy-assets.cmake
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_dependencies(${ENGINE} ${ENGINE}-assets)
+87
View File
@@ -0,0 +1,87 @@
SIL OPEN FONT LICENSE
Version 1.1, 26 February 2007
Copyright (C) 2010-2023 Steve Matteson
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide development
of collaborative font projects, to support the font creation efforts of academic
and linguistic communities, and to provide a free and open framework in which
fonts may be shared and improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The fonts,
including any derivative works, can be bundled, embedded, redistributed and/or
sold with any software provided that any reserved names are not used by
derivative works. The fonts and derivatives, however, cannot be released under
any other type of license. The requirement for fonts to remain under this
license does not apply to any document created using the fonts or their
derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright Holder(s)
under this license and clearly marked as such. This may include source files,
build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the copyright
statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting, or
substituting in part or in whole any of the components of the Original
Version, by changing formats or by porting the Font Software to a new
environment.
"Author" refers to any designer, engineer, programmer, technical writer or other
person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a copy of
the Font Software, to use, study, copy, merge, embed, modify, redistribute, and
sell modified and unmodified copies of the Font Software, subject to the
following conditions:
Neither the Font Software nor any of its individual components, in Original
or Modified Versions, may be sold by itself.
Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be included
either as stand-alone text files, human-readable headers or in the
appropriate machine-readable metadata fields within text or binary files as
long as those fields can be easily viewed by the user.
No Modified Version of the Font Software may use the Reserved Font Name(s)
unless explicit written permission is granted by the corresponding Copyright
Holder. This restriction only applies to the primary font name as presented
to the users.
The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software
shall not be used to promote, endorse or advertise any Modified Version,
except to acknowledge the contribution(s) of the Copyright Holder(s) and the
Author(s) or with their explicit written permission.
The Font Software, modified or unmodified, in part or in whole, must be
distributed entirely under this license, and must not be distributed under
any other license. The requirement for fonts to remain under this license
does not apply to any document created using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR
OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT,
INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR
FROM OTHER DEALINGS IN THE FONT SOFTWARE.
+415
View File
@@ -0,0 +1,415 @@
info face="Open Sans Regular" size=62 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=8,8,8,8 spacing=-2,-20
common lineHeight=82 base=67 scaleW=512 scaleH=512 pages=1 packed=0
page id=0 file="open-sans.png"
chars count=97
char id=0 x=377 y=326 width=41 height=60 xoffset=-2 yoffset=15 xadvance=51 page=0 chnl=0
char id=10 x=0 y=0 width=0 height=0 xoffset=-8 yoffset=0 xadvance=14 page=0 chnl=0
char id=32 x=0 y=0 width=0 height=0 xoffset=-8 yoffset=0 xadvance=30 page=0 chnl=0
char id=33 x=142 y=205 width=24 height=61 xoffset=-4 yoffset=15 xadvance=31 page=0 chnl=0
char id=34 x=319 y=437 width=33 height=32 xoffset=-4 yoffset=15 xadvance=39 page=0 chnl=0
char id=35 x=323 y=326 width=54 height=60 xoffset=-7 yoffset=15 xadvance=54 page=0 chnl=0
char id=36 x=349 y=0 width=44 height=67 xoffset=-5 yoffset=12 xadvance=49 page=0 chnl=0
char id=37 x=372 y=143 width=61 height=62 xoffset=-5 yoffset=14 xadvance=65 page=0 chnl=0
char id=38 x=433 y=143 width=58 height=62 xoffset=-5 yoffset=14 xadvance=59 page=0 chnl=0
char id=39 x=352 y=437 width=22 height=32 xoffset=-4 yoffset=15 xadvance=28 page=0 chnl=0
char id=40 x=151 y=0 width=31 height=70 xoffset=-6 yoffset=15 xadvance=32 page=0 chnl=0
char id=41 x=182 y=0 width=31 height=70 xoffset=-7 yoffset=15 xadvance=32 page=0 chnl=0
char id=42 x=273 y=437 width=46 height=44 xoffset=-6 yoffset=12 xadvance=48 page=0 chnl=0
char id=43 x=178 y=437 width=47 height=47 xoffset=-6 yoffset=22 xadvance=49 page=0 chnl=0
char id=44 x=419 y=437 width=26 height=31 xoffset=-7 yoffset=52 xadvance=29 page=0 chnl=0
char id=45 x=45 y=486 width=32 height=21 xoffset=-6 yoffset=40 xadvance=34 page=0 chnl=0
char id=46 x=474 y=437 width=25 height=25 xoffset=-4 yoffset=51 xadvance=31 page=0 chnl=0
char id=47 x=245 y=326 width=39 height=60 xoffset=-8 yoffset=15 xadvance=37 page=0 chnl=0
char id=48 x=285 y=143 width=45 height=62 xoffset=-5 yoffset=14 xadvance=49 page=0 chnl=0
char id=49 x=467 y=266 width=33 height=60 xoffset=-3 yoffset=15 xadvance=49 page=0 chnl=0
char id=50 x=51 y=205 width=46 height=61 xoffset=-6 yoffset=14 xadvance=49 page=0 chnl=0
char id=51 x=103 y=143 width=46 height=62 xoffset=-6 yoffset=14 xadvance=49 page=0 chnl=0
char id=52 x=150 y=326 width=49 height=60 xoffset=-7 yoffset=15 xadvance=49 page=0 chnl=0
char id=53 x=97 y=205 width=45 height=61 xoffset=-5 yoffset=15 xadvance=49 page=0 chnl=0
char id=54 x=149 y=143 width=46 height=62 xoffset=-5 yoffset=14 xadvance=49 page=0 chnl=0
char id=55 x=199 y=326 width=46 height=60 xoffset=-6 yoffset=15 xadvance=49 page=0 chnl=0
char id=56 x=195 y=143 width=45 height=62 xoffset=-5 yoffset=14 xadvance=49 page=0 chnl=0
char id=57 x=240 y=143 width=45 height=62 xoffset=-5 yoffset=14 xadvance=49 page=0 chnl=0
char id=58 x=174 y=386 width=25 height=51 xoffset=-4 yoffset=25 xadvance=31 page=0 chnl=0
char id=59 x=475 y=205 width=26 height=58 xoffset=-6 yoffset=25 xadvance=31 page=0 chnl=0
char id=60 x=88 y=437 width=45 height=47 xoffset=-5 yoffset=21 xadvance=49 page=0 chnl=0
char id=61 x=374 y=437 width=45 height=32 xoffset=-5 yoffset=29 xadvance=49 page=0 chnl=0
char id=62 x=133 y=437 width=45 height=47 xoffset=-5 yoffset=21 xadvance=49 page=0 chnl=0
char id=63 x=330 y=143 width=42 height=62 xoffset=-8 yoffset=14 xadvance=41 page=0 chnl=0
char id=64 x=393 y=0 width=66 height=66 xoffset=-5 yoffset=15 xadvance=70 page=0 chnl=0
char id=65 x=166 y=205 width=55 height=60 xoffset=-8 yoffset=15 xadvance=53 page=0 chnl=0
char id=66 x=221 y=205 width=47 height=60 xoffset=-2 yoffset=15 xadvance=54 page=0 chnl=0
char id=67 x=384 y=78 width=51 height=62 xoffset=-5 yoffset=14 xadvance=53 page=0 chnl=0
char id=68 x=268 y=205 width=52 height=60 xoffset=-2 yoffset=15 xadvance=59 page=0 chnl=0
char id=69 x=320 y=205 width=41 height=60 xoffset=-2 yoffset=15 xadvance=48 page=0 chnl=0
char id=70 x=361 y=205 width=41 height=60 xoffset=-2 yoffset=15 xadvance=46 page=0 chnl=0
char id=71 x=435 y=78 width=54 height=62 xoffset=-5 yoffset=14 xadvance=59 page=0 chnl=0
char id=72 x=402 y=205 width=50 height=60 xoffset=-2 yoffset=15 xadvance=60 page=0 chnl=0
char id=73 x=452 y=205 width=23 height=60 xoffset=-3 yoffset=15 xadvance=31 page=0 chnl=0
char id=74 x=114 y=0 width=37 height=71 xoffset=-13 yoffset=15 xadvance=31 page=0 chnl=0
char id=75 x=0 y=266 width=48 height=60 xoffset=-2 yoffset=15 xadvance=52 page=0 chnl=0
char id=76 x=48 y=266 width=41 height=60 xoffset=-2 yoffset=15 xadvance=46 page=0 chnl=0
char id=77 x=89 y=266 width=60 height=60 xoffset=-2 yoffset=15 xadvance=70 page=0 chnl=0
char id=78 x=149 y=266 width=51 height=60 xoffset=-2 yoffset=15 xadvance=61 page=0 chnl=0
char id=79 x=0 y=143 width=58 height=62 xoffset=-5 yoffset=14 xadvance=62 page=0 chnl=0
char id=80 x=200 y=266 width=44 height=60 xoffset=-2 yoffset=15 xadvance=51 page=0 chnl=0
char id=81 x=56 y=0 width=58 height=72 xoffset=-5 yoffset=14 xadvance=62 page=0 chnl=0
char id=82 x=244 y=266 width=47 height=60 xoffset=-2 yoffset=15 xadvance=52 page=0 chnl=0
char id=83 x=58 y=143 width=45 height=62 xoffset=-5 yoffset=14 xadvance=48 page=0 chnl=0
char id=84 x=291 y=266 width=50 height=60 xoffset=-8 yoffset=15 xadvance=48 page=0 chnl=0
char id=85 x=0 y=205 width=51 height=61 xoffset=-3 yoffset=15 xadvance=59 page=0 chnl=0
char id=86 x=341 y=266 width=53 height=60 xoffset=-8 yoffset=15 xadvance=51 page=0 chnl=0
char id=87 x=394 y=266 width=73 height=60 xoffset=-8 yoffset=15 xadvance=71 page=0 chnl=0
char id=88 x=0 y=326 width=52 height=60 xoffset=-8 yoffset=15 xadvance=50 page=0 chnl=0
char id=89 x=52 y=326 width=51 height=60 xoffset=-8 yoffset=15 xadvance=49 page=0 chnl=0
char id=90 x=103 y=326 width=47 height=60 xoffset=-6 yoffset=15 xadvance=49 page=0 chnl=0
char id=91 x=213 y=0 width=31 height=70 xoffset=-4 yoffset=15 xadvance=34 page=0 chnl=0
char id=92 x=284 y=326 width=39 height=60 xoffset=-8 yoffset=15 xadvance=37 page=0 chnl=0
char id=93 x=244 y=0 width=31 height=70 xoffset=-7 yoffset=15 xadvance=34 page=0 chnl=0
char id=94 x=225 y=437 width=48 height=44 xoffset=-7 yoffset=14 xadvance=48 page=0 chnl=0
char id=95 x=77 y=486 width=46 height=20 xoffset=-9 yoffset=65 xadvance=42 page=0 chnl=0
char id=96 x=445 y=437 width=29 height=26 xoffset=3 yoffset=11 xadvance=50 page=0 chnl=0
char id=97 x=455 y=326 width=44 height=51 xoffset=-6 yoffset=25 xadvance=48 page=0 chnl=0
char id=98 x=92 y=78 width=46 height=64 xoffset=-3 yoffset=12 xadvance=52 page=0 chnl=0
char id=99 x=0 y=386 width=41 height=51 xoffset=-5 yoffset=25 xadvance=44 page=0 chnl=0
char id=100 x=138 y=78 width=46 height=64 xoffset=-5 yoffset=12 xadvance=52 page=0 chnl=0
char id=101 x=41 y=386 width=45 height=51 xoffset=-5 yoffset=25 xadvance=49 page=0 chnl=0
char id=102 x=231 y=78 width=42 height=63 xoffset=-8 yoffset=12 xadvance=35 page=0 chnl=0
char id=103 x=459 y=0 width=48 height=65 xoffset=-7 yoffset=25 xadvance=48 page=0 chnl=0
char id=104 x=273 y=78 width=45 height=63 xoffset=-3 yoffset=12 xadvance=52 page=0 chnl=0
char id=105 x=489 y=78 width=22 height=62 xoffset=-3 yoffset=13 xadvance=30 page=0 chnl=0
char id=106 x=22 y=0 width=34 height=77 xoffset=-12 yoffset=13 xadvance=30 page=0 chnl=0
char id=107 x=318 y=78 width=44 height=63 xoffset=-3 yoffset=12 xadvance=47 page=0 chnl=0
char id=108 x=362 y=78 width=22 height=63 xoffset=-3 yoffset=12 xadvance=30 page=0 chnl=0
char id=109 x=199 y=386 width=64 height=50 xoffset=-3 yoffset=25 xadvance=72 page=0 chnl=0
char id=110 x=263 y=386 width=45 height=50 xoffset=-3 yoffset=25 xadvance=52 page=0 chnl=0
char id=111 x=86 y=386 width=47 height=51 xoffset=-5 yoffset=25 xadvance=51 page=0 chnl=0
char id=112 x=0 y=78 width=46 height=65 xoffset=-3 yoffset=25 xadvance=52 page=0 chnl=0
char id=113 x=46 y=78 width=46 height=65 xoffset=-5 yoffset=25 xadvance=52 page=0 chnl=0
char id=114 x=308 y=386 width=36 height=50 xoffset=-3 yoffset=25 xadvance=39 page=0 chnl=0
char id=115 x=133 y=386 width=41 height=51 xoffset=-5 yoffset=25 xadvance=44 page=0 chnl=0
char id=116 x=418 y=326 width=37 height=58 xoffset=-8 yoffset=18 xadvance=36 page=0 chnl=0
char id=117 x=344 y=386 width=45 height=50 xoffset=-4 yoffset=26 xadvance=52 page=0 chnl=0
char id=118 x=389 y=386 width=47 height=49 xoffset=-8 yoffset=26 xadvance=45 page=0 chnl=0
char id=119 x=436 y=386 width=64 height=49 xoffset=-8 yoffset=26 xadvance=62 page=0 chnl=0
char id=120 x=0 y=437 width=47 height=49 xoffset=-8 yoffset=26 xadvance=46 page=0 chnl=0
char id=121 x=184 y=78 width=47 height=64 xoffset=-8 yoffset=26 xadvance=45 page=0 chnl=0
char id=122 x=47 y=437 width=41 height=49 xoffset=-6 yoffset=26 xadvance=43 page=0 chnl=0
char id=123 x=275 y=0 width=37 height=70 xoffset=-7 yoffset=15 xadvance=38 page=0 chnl=0
char id=124 x=0 y=0 width=22 height=78 xoffset=6 yoffset=12 xadvance=48 page=0 chnl=0
char id=125 x=312 y=0 width=37 height=70 xoffset=-6 yoffset=15 xadvance=38 page=0 chnl=0
char id=126 x=0 y=486 width=45 height=24 xoffset=-5 yoffset=33 xadvance=49 page=0 chnl=0
kernings count=313
kerning first=70 second=46 amount=-4
kerning first=79 second=86 amount=-1
kerning first=107 second=113 amount=-1
kerning first=39 second=65 amount=-4
kerning first=86 second=79 amount=-1
kerning first=89 second=122 amount=-1
kerning first=68 second=88 amount=-1
kerning first=75 second=81 amount=-1
kerning first=84 second=121 amount=-1
kerning first=88 second=71 amount=-1
kerning first=89 second=67 amount=-1
kerning first=121 second=44 amount=-2
kerning first=39 second=99 amount=-4
kerning first=86 second=113 amount=-1
kerning first=65 second=79 amount=-1
kerning first=87 second=109 amount=-1
kerning first=79 second=65 amount=-1
kerning first=67 second=71 amount=-1
kerning first=89 second=101 amount=-3
kerning first=84 second=100 amount=-4
kerning first=44 second=79 amount=-3
kerning first=89 second=46 amount=-4
kerning first=84 second=45 amount=-2
kerning first=46 second=71 amount=-3
kerning first=40 second=74 amount=6
kerning first=79 second=44 amount=-2
kerning first=98 second=120 amount=-1
kerning first=68 second=46 amount=-2
kerning first=39 second=112 amount=-2
kerning first=84 second=79 amount=-1
kerning first=34 second=111 amount=-4
kerning first=66 second=88 amount=-1
kerning first=86 second=71 amount=-1
kerning first=89 second=114 amount=-2
kerning first=87 second=67 amount=-1
kerning first=119 second=44 amount=-2
kerning first=84 second=113 amount=-4
kerning first=65 second=71 amount=-1
kerning first=46 second=84 amount=-4
kerning first=87 second=101 amount=-1
kerning first=111 second=34 amount=-1
kerning first=87 second=46 amount=-3
kerning first=112 second=119 amount=-1
kerning first=44 second=71 amount=-3
kerning first=101 second=121 amount=-1
kerning first=114 second=111 amount=-1
kerning first=66 second=46 amount=-2
kerning first=84 second=71 amount=-1
kerning first=65 second=84 amount=-4
kerning first=87 second=114 amount=-1
kerning first=34 second=103 amount=-2
kerning first=86 second=63 amount=1
kerning first=44 second=84 amount=-4
kerning first=86 second=97 amount=-1
kerning first=109 second=34 amount=-1
kerning first=85 second=46 amount=-1
kerning first=39 second=117 amount=-2
kerning first=90 second=81 amount=-1
kerning first=84 second=84 amount=1
kerning first=120 second=100 amount=-1
kerning first=114 second=103 amount=-1
kerning first=84 second=118 amount=-1
kerning first=86 second=110 amount=-1
kerning first=84 second=63 amount=1
kerning first=46 second=89 amount=-4
kerning first=111 second=39 amount=-1
kerning first=84 second=97 amount=-5
kerning first=120 second=113 amount=-1
kerning first=81 second=88 amount=-1
kerning first=88 second=81 amount=-1
kerning first=76 second=87 amount=-1
kerning first=39 second=109 amount=-2
kerning first=65 second=89 amount=-4
kerning first=67 second=81 amount=-1
kerning first=65 second=34 amount=-4
kerning first=89 second=111 amount=-3
kerning first=84 second=110 amount=-3
kerning first=44 second=89 amount=-4
kerning first=109 second=39 amount=-1
kerning first=46 second=81 amount=-3
kerning first=34 second=87 amount=1
kerning first=111 second=120 amount=-1
kerning first=81 second=46 amount=-2
kerning first=79 second=88 amount=-1
kerning first=101 second=118 amount=-1
kerning first=86 second=81 amount=-1
kerning first=68 second=90 amount=-1
kerning first=76 second=79 amount=-1
kerning first=121 second=46 amount=-2
kerning first=39 second=101 amount=-4
kerning first=86 second=115 amount=-1
kerning first=65 second=81 amount=-1
kerning first=87 second=111 amount=-1
kerning first=34 second=100 amount=-4
kerning first=89 second=103 amount=-1
kerning first=44 second=81 amount=-3
kerning first=79 second=46 amount=-2
kerning first=98 second=122 amount=-1
kerning first=39 second=114 amount=-2
kerning first=84 second=81 amount=-1
kerning first=91 second=74 amount=6
kerning first=34 second=113 amount=-4
kerning first=66 second=90 amount=-1
kerning first=114 second=100 amount=-1
kerning first=65 second=39 amount=-4
kerning first=119 second=46 amount=-2
kerning first=84 second=115 amount=-4
kerning first=76 second=71 amount=-1
kerning first=46 second=86 amount=-4
kerning first=87 second=103 amount=-1
kerning first=112 second=121 amount=-1
kerning first=101 second=34 amount=-1
kerning first=114 second=113 amount=-1
kerning first=118 second=63 amount=1
kerning first=76 second=84 amount=-1
kerning first=65 second=86 amount=-2
kerning first=107 second=99 amount=-1
kerning first=86 second=65 amount=-2
kerning first=75 second=67 amount=-1
kerning first=44 second=86 amount=-4
kerning first=86 second=99 amount=-1
kerning first=34 second=84 amount=1
kerning first=86 second=44 amount=-3
kerning first=99 second=34 amount=1
kerning first=68 second=87 amount=-1
kerning first=84 second=120 amount=-1
kerning first=86 second=112 amount=-1
kerning first=84 second=65 amount=-4
kerning first=34 second=97 amount=-2
kerning first=118 second=34 amount=2
kerning first=89 second=100 amount=-3
kerning first=84 second=99 amount=-4
kerning first=101 second=39 amount=-1
kerning first=84 second=44 amount=-4
kerning first=97 second=34 amount=-1
kerning first=81 second=90 amount=-1
kerning first=76 second=89 amount=-2
kerning first=89 second=79 amount=-1
kerning first=98 second=119 amount=-1
kerning first=39 second=111 amount=-4
kerning first=76 second=34 amount=-5
kerning first=34 second=110 amount=-2
kerning first=66 second=87 amount=-1
kerning first=114 second=97 amount=-1
kerning first=89 second=113 amount=-3
kerning first=84 second=112 amount=-3
kerning first=116 second=34 amount=1
kerning first=87 second=100 amount=-1
kerning first=34 second=89 amount=1
kerning first=99 second=39 amount=1
kerning first=111 second=122 amount=-1
kerning first=112 second=118 amount=-1
kerning first=79 second=90 amount=-1
kerning first=101 second=120 amount=-1
kerning first=87 second=79 amount=-1
kerning first=76 second=81 amount=-1
kerning first=89 second=71 amount=-1
kerning first=39 second=103 amount=-2
kerning first=86 second=117 amount=-1
kerning first=90 second=67 amount=-1
kerning first=87 second=113 amount=-1
kerning first=118 second=39 amount=2
kerning first=80 second=65 amount=-3
kerning first=114 second=34 amount=2
kerning first=123 second=74 amount=6
kerning first=70 second=63 amount=1
kerning first=97 second=39 amount=-1
kerning first=80 second=44 amount=-8
kerning first=76 second=39 amount=-5
kerning first=34 second=115 amount=-2
kerning first=120 second=99 amount=-1
kerning first=87 second=71 amount=-1
kerning first=68 second=84 amount=-2
kerning first=84 second=117 amount=-3
kerning first=88 second=67 amount=-1
kerning first=89 second=63 amount=1
kerning first=86 second=109 amount=-1
kerning first=116 second=39 amount=1
kerning first=67 second=67 amount=-1
kerning first=89 second=97 amount=-3
kerning first=112 second=34 amount=-1
kerning first=46 second=67 amount=-3
kerning first=81 second=87 amount=-1
kerning first=76 second=86 amount=-1
kerning first=66 second=84 amount=-2
kerning first=107 second=101 amount=-1
kerning first=86 second=67 amount=-1
kerning first=118 second=44 amount=-2
kerning first=89 second=110 amount=-2
kerning first=87 second=63 amount=1
kerning first=84 second=109 amount=-3
kerning first=114 second=39 amount=2
kerning first=45 second=84 amount=-2
kerning first=39 second=87 amount=1
kerning first=86 second=101 amount=-1
kerning first=65 second=67 amount=-1
kerning first=87 second=97 amount=-1
kerning first=110 second=34 amount=-1
kerning first=34 second=86 amount=1
kerning first=86 second=46 amount=-3
kerning first=111 second=119 amount=-1
kerning first=44 second=67 amount=-3
kerning first=79 second=87 amount=-1
kerning first=68 second=89 amount=-1
kerning first=34 second=65 amount=-4
kerning first=84 second=122 amount=-2
kerning first=39 second=100 amount=-4
kerning first=86 second=114 amount=-1
kerning first=84 second=67 amount=-1
kerning first=87 second=110 amount=-1
kerning first=34 second=99 amount=-4
kerning first=112 second=39 amount=-1
kerning first=84 second=101 amount=-4
kerning first=84 second=46 amount=-4
kerning first=89 second=81 amount=-1
kerning first=98 second=121 amount=-1
kerning first=39 second=113 amount=-4
kerning first=34 second=112 amount=-2
kerning first=66 second=89 amount=-1
kerning first=114 second=99 amount=-1
kerning first=89 second=115 amount=-2
kerning first=84 second=114 amount=-3
kerning first=46 second=85 amount=-1
kerning first=110 second=39 amount=-1
kerning first=112 second=120 amount=-1
kerning first=101 second=122 amount=-1
kerning first=80 second=88 amount=-1
kerning first=87 second=81 amount=-1
kerning first=81 second=84 amount=-2
kerning first=87 second=115 amount=-1
kerning first=44 second=85 amount=-1
kerning first=70 second=65 amount=-1
kerning first=39 second=84 amount=1
kerning first=104 second=34 amount=-1
kerning first=80 second=46 amount=-8
kerning first=121 second=63 amount=1
kerning first=34 second=117 amount=-2
kerning first=70 second=44 amount=-4
kerning first=79 second=84 amount=-2
kerning first=107 second=111 amount=-1
kerning first=120 second=101 amount=-1
kerning first=68 second=86 amount=-1
kerning first=75 second=79 amount=-1
kerning first=84 second=119 amount=-1
kerning first=89 second=65 amount=-4
kerning first=39 second=97 amount=-2
kerning first=86 second=111 amount=-1
kerning first=89 second=99 amount=-3
kerning first=68 second=65 amount=-1
kerning first=89 second=44 amount=-4
kerning first=102 second=34 amount=4
kerning first=119 second=63 amount=1
kerning first=81 second=89 amount=-1
kerning first=98 second=118 amount=-1
kerning first=68 second=44 amount=-2
kerning first=39 second=110 amount=-2
kerning first=34 second=109 amount=-2
kerning first=66 second=86 amount=-1
kerning first=118 second=46 amount=-2
kerning first=89 second=112 amount=-2
kerning first=87 second=65 amount=-2
kerning first=75 second=71 amount=-1
kerning first=84 second=111 amount=-4
kerning first=69 second=74 amount=4
kerning first=76 second=67 amount=-1
kerning first=121 second=34 amount=2
kerning first=39 second=89 amount=1
kerning first=86 second=103 amount=-1
kerning first=87 second=99 amount=-1
kerning first=104 second=39 amount=-1
kerning first=66 second=65 amount=-1
kerning first=87 second=44 amount=-3
kerning first=111 second=121 amount=-1
kerning first=79 second=89 amount=-1
kerning first=101 second=119 amount=-1
kerning first=66 second=44 amount=-2
kerning first=87 second=112 amount=-1
kerning first=34 second=101 amount=-4
kerning first=85 second=65 amount=-1
kerning first=119 second=34 amount=2
kerning first=84 second=103 amount=-4
kerning first=102 second=39 amount=4
kerning first=85 second=44 amount=-1
kerning first=98 second=34 amount=-1
kerning first=39 second=115 amount=-2
kerning first=90 second=79 amount=-1
kerning first=34 second=114 amount=-2
kerning first=114 second=101 amount=-1
kerning first=89 second=117 amount=-2
kerning first=121 second=39 amount=2
kerning first=65 second=74 amount=8
kerning first=46 second=87 amount=-4
kerning first=112 second=122 amount=-1
kerning first=120 second=111 amount=-1
kerning first=80 second=90 amount=-1
kerning first=81 second=86 amount=-1
kerning first=88 second=79 amount=-1
kerning first=76 second=85 amount=-1
kerning first=90 second=71 amount=-1
kerning first=65 second=87 amount=-2
kerning first=87 second=117 amount=-1
kerning first=107 second=100 amount=-1
kerning first=67 second=79 amount=-1
kerning first=89 second=109 amount=-2
kerning first=119 second=39 amount=2
kerning first=81 second=65 amount=-1
kerning first=44 second=87 amount=-4
kerning first=39 second=86 amount=1
kerning first=86 second=100 amount=-1
kerning first=46 second=79 amount=-3
kerning first=98 second=39 amount=-1
kerning first=111 second=118 amount=-1
kerning first=81 second=44 amount=-2
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

@@ -4,14 +4,14 @@ layout(location = 0) out vec4 color;
in vec4 v_color;
in vec2 v_textureCoordinates;
in flat float v_textureIndex;
in flat uint v_textureIndex;
uniform sampler2D u_textures[32];
void main()
{
vec4 textureColor = v_color;
switch(int(v_textureIndex)) {
switch(v_textureIndex) {
case 0: break; // Texture unit 0 is reserved for no texture
case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
case 2: textureColor *= texture(u_textures[2], v_textureCoordinates); break;
+19
View File
@@ -0,0 +1,19 @@
#version 450 core
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in uint a_textureIndex;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat uint v_textureIndex;
void main()
{
v_color = a_color;
v_textureCoordinates = a_textureCoordinates;
v_textureIndex = a_textureIndex;
// Vclip = Model transform * Vlocal
gl_Position = vec4(a_position, 1.0f);
}
+57
View File
@@ -0,0 +1,57 @@
#version 450 core
layout(location = 0) out vec4 albedoSpec; // RGB diffuse color
layout(location = 1) out vec4 position;
layout(location = 2) out vec4 normal;
in vec3 v_position;
in vec3 v_normal;
in vec4 v_color;
in vec2 v_textureCoordinates;
in flat uint v_textureIndex;
uniform sampler2D u_textures[32];
void main()
{
vec4 textureColor = v_color;
switch(v_textureIndex) {
case 0: break; // Texture unit 0 is reserved for no texture
case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
case 2: textureColor *= texture(u_textures[2], v_textureCoordinates); break;
case 3: textureColor *= texture(u_textures[3], v_textureCoordinates); break;
case 4: textureColor *= texture(u_textures[4], v_textureCoordinates); break;
case 5: textureColor *= texture(u_textures[5], v_textureCoordinates); break;
case 6: textureColor *= texture(u_textures[6], v_textureCoordinates); break;
case 7: textureColor *= texture(u_textures[7], v_textureCoordinates); break;
case 8: textureColor *= texture(u_textures[8], v_textureCoordinates); break;
case 9: textureColor *= texture(u_textures[9], v_textureCoordinates); break;
case 10: textureColor *= texture(u_textures[10], v_textureCoordinates); break;
case 11: textureColor *= texture(u_textures[11], v_textureCoordinates); break;
case 12: textureColor *= texture(u_textures[12], v_textureCoordinates); break;
case 13: textureColor *= texture(u_textures[13], v_textureCoordinates); break;
case 14: textureColor *= texture(u_textures[14], v_textureCoordinates); break;
case 15: textureColor *= texture(u_textures[15], v_textureCoordinates); break;
case 16: textureColor *= texture(u_textures[16], v_textureCoordinates); break;
case 17: textureColor *= texture(u_textures[17], v_textureCoordinates); break;
case 18: textureColor *= texture(u_textures[18], v_textureCoordinates); break;
case 19: textureColor *= texture(u_textures[19], v_textureCoordinates); break;
case 20: textureColor *= texture(u_textures[20], v_textureCoordinates); break;
case 21: textureColor *= texture(u_textures[21], v_textureCoordinates); break;
case 22: textureColor *= texture(u_textures[22], v_textureCoordinates); break;
case 23: textureColor *= texture(u_textures[23], v_textureCoordinates); break;
case 24: textureColor *= texture(u_textures[24], v_textureCoordinates); break;
case 25: textureColor *= texture(u_textures[25], v_textureCoordinates); break;
case 26: textureColor *= texture(u_textures[26], v_textureCoordinates); break;
case 27: textureColor *= texture(u_textures[27], v_textureCoordinates); break;
case 28: textureColor *= texture(u_textures[28], v_textureCoordinates); break;
case 29: textureColor *= texture(u_textures[29], v_textureCoordinates); break;
case 30: textureColor *= texture(u_textures[30], v_textureCoordinates); break;
case 31: textureColor *= texture(u_textures[31], v_textureCoordinates); break;
}
albedoSpec.rgb = textureColor.rgb;
albedoSpec.a = 1.0; // TODO: read specular from model material
position = vec4(v_position, 1.0f);
normal = vec4(normalize(v_normal), 1.0f);
}
+30
View File
@@ -0,0 +1,30 @@
#version 450 core
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_normal;
layout(location = 2) in vec4 a_color;
layout(location = 3) in vec2 a_textureCoordinates;
layout(location = 4) in uint a_textureIndex;
out vec3 v_position;
out vec3 v_normal;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat uint v_textureIndex;
layout(std140, binding = 0) uniform Camera
{
mat4 u_projectionView;
vec3 u_position;
};
void main()
{
v_position = a_position;
v_normal = a_normal;
v_color = a_color;
v_textureCoordinates = a_textureCoordinates;
v_textureIndex = a_textureIndex;
// Vclip = Camera projection * Camera view * Model transform * Vlocal
gl_Position = u_projectionView * vec4(a_position, 1.0f);
}
+49
View File
@@ -0,0 +1,49 @@
#version 450 core
layout(location = 0) out vec4 color;
in vec4 v_color;
in vec3 v_textureCoordinates;
in flat uint v_textureIndex;
uniform samplerCube u_textures[32];
void main()
{
vec4 textureColor = v_color;
switch(v_textureIndex) {
case 0: break; // Texture unit 0 is reserved for no texture
case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
case 2: textureColor *= texture(u_textures[2], v_textureCoordinates); break;
case 3: textureColor *= texture(u_textures[3], v_textureCoordinates); break;
case 4: textureColor *= texture(u_textures[4], v_textureCoordinates); break;
case 5: textureColor *= texture(u_textures[5], v_textureCoordinates); break;
case 6: textureColor *= texture(u_textures[6], v_textureCoordinates); break;
case 7: textureColor *= texture(u_textures[7], v_textureCoordinates); break;
case 8: textureColor *= texture(u_textures[8], v_textureCoordinates); break;
case 9: textureColor *= texture(u_textures[9], v_textureCoordinates); break;
case 10: textureColor *= texture(u_textures[10], v_textureCoordinates); break;
case 11: textureColor *= texture(u_textures[11], v_textureCoordinates); break;
case 12: textureColor *= texture(u_textures[12], v_textureCoordinates); break;
case 13: textureColor *= texture(u_textures[13], v_textureCoordinates); break;
case 14: textureColor *= texture(u_textures[14], v_textureCoordinates); break;
case 15: textureColor *= texture(u_textures[15], v_textureCoordinates); break;
case 16: textureColor *= texture(u_textures[16], v_textureCoordinates); break;
case 17: textureColor *= texture(u_textures[17], v_textureCoordinates); break;
case 18: textureColor *= texture(u_textures[18], v_textureCoordinates); break;
case 19: textureColor *= texture(u_textures[19], v_textureCoordinates); break;
case 20: textureColor *= texture(u_textures[20], v_textureCoordinates); break;
case 21: textureColor *= texture(u_textures[21], v_textureCoordinates); break;
case 22: textureColor *= texture(u_textures[22], v_textureCoordinates); break;
case 23: textureColor *= texture(u_textures[23], v_textureCoordinates); break;
case 24: textureColor *= texture(u_textures[24], v_textureCoordinates); break;
case 25: textureColor *= texture(u_textures[25], v_textureCoordinates); break;
case 26: textureColor *= texture(u_textures[26], v_textureCoordinates); break;
case 27: textureColor *= texture(u_textures[27], v_textureCoordinates); break;
case 28: textureColor *= texture(u_textures[28], v_textureCoordinates); break;
case 29: textureColor *= texture(u_textures[29], v_textureCoordinates); break;
case 30: textureColor *= texture(u_textures[30], v_textureCoordinates); break;
case 31: textureColor *= texture(u_textures[31], v_textureCoordinates); break;
}
color = textureColor;
}
@@ -2,19 +2,18 @@
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in float a_textureIndex;
layout(location = 2) in uint a_textureIndex;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat float v_textureIndex;
out vec3 v_textureCoordinates;
out flat uint v_textureIndex;
uniform mat4 u_projectionView;
void main()
{
v_color = a_color;
v_textureCoordinates = a_textureCoordinates;
v_textureCoordinates = a_position;
v_textureIndex = a_textureIndex;
// Vclip = Camera projection * Camera view * Model transform * Vlocal
gl_Position = u_projectionView * vec4(a_position, 1.0f);
+2 -2
View File
@@ -4,7 +4,7 @@ layout(location = 0) out vec4 color;
in vec4 v_color;
in vec2 v_textureCoordinates;
in flat float v_textureIndex;
in flat uint v_textureIndex;
in float v_width;
in float v_edge;
in float v_borderWidth;
@@ -25,7 +25,7 @@ float alpha(float textureAlpha)
void main()
{
vec4 textureColor = v_color;
switch(int(v_textureIndex)) {
switch(v_textureIndex) {
case 0: break; // Texture unit 0 is reserved for no texture
// case 1: textureColor.a = 1; break;
case 1: textureColor.a = alpha(texture(u_textures[1], v_textureCoordinates).a); break;
+2 -5
View File
@@ -3,7 +3,7 @@
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in float a_textureIndex;
layout(location = 3) in uint a_textureIndex;
layout(location = 4) in float a_width;
layout(location = 5) in float a_edge;
layout(location = 6) in float a_borderWidth;
@@ -13,7 +13,7 @@ layout(location = 9) in float a_offset;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat float v_textureIndex;
out flat uint v_textureIndex;
out float v_width;
out float v_edge;
out float v_borderWidth;
@@ -33,7 +33,4 @@ void main()
v_borderColor = a_borderColor;
v_offset = a_offset;
gl_Position = vec4(a_position, 1.0f);
// Vclip = Camera projection * Camera view * Model transform * Vlocal
// gl_Position = u_projectionView * vec4(a_position, 1.0f);
}
+17
View File
@@ -0,0 +1,17 @@
#version 450 core
layout(location = 0) out vec4 color;
in vec4 v_color;
in vec3 v_textureCoordinates;
in flat uint v_textureIndex;
uniform samplerCube u_textures[32];
void main()
{
// Prevent u_textures variable from getting optimized away
if (texture(u_textures[0], vec3(0.0f)).x > 99999.0f) { discard; }
color = v_color;
}
+24
View File
@@ -0,0 +1,24 @@
#version 450 core
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in uint a_textureIndex;
out vec4 v_color;
out vec3 v_textureCoordinates;
out flat uint v_textureIndex;
layout(std140, binding = 0) uniform Camera
{
mat4 u_projectionView;
vec3 u_position;
};
void main()
{
v_color = a_color;
v_textureCoordinates = a_position;
v_textureIndex = a_textureIndex;
// Vclip = Camera projection * Camera view * Model transform * Vlocal
gl_Position = u_projectionView * vec4(a_position, 1.0f);
}
+78
View File
@@ -0,0 +1,78 @@
#version 450 core
layout(location = 0) out vec4 color;
in vec4 v_color;
in vec2 v_textureCoordinates;
in flat uint v_textureIndex;
uniform sampler2D u_textures[32];
// -----------------------------------------
layout(std140, binding = 0) uniform Camera {
mat4 u_projectionView;
vec3 u_position;
};
// -----------------------------------------
struct DirectionalLight {
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
const int MAX_DIRECTIONAL_LIGHTS = 4;
layout(std430, binding = 0) buffer DirectionalLights {
DirectionalLight u_directionalLight[MAX_DIRECTIONAL_LIGHTS];
};
// -----------------------------------------
void main()
{
float isObject = texture(u_textures[v_textureIndex + 1], v_textureCoordinates).a;
if (isObject == 0.0f) {
color = vec4(0,0,0,0);
return;
}
vec3 albedo = texture(u_textures[v_textureIndex + 0], v_textureCoordinates).rgb;
float specular = texture(u_textures[v_textureIndex + 0], v_textureCoordinates).a;
vec3 position = texture(u_textures[v_textureIndex + 1], v_textureCoordinates).rgb;
vec3 normal = texture(u_textures[v_textureIndex + 2], v_textureCoordinates).rgb;
vec3 lighting = vec3(0.0f, 0.0f, 0.0f);//albedo * v_color.xyz;
vec3 viewDirection = normalize(u_position - position);
// Loop through all directional lights
for (int i = 0; i < MAX_DIRECTIONAL_LIGHTS; ++i) {
// Diffuse
vec3 lightDirection = normalize(-u_directionalLight[i].direction);
float diffuse = max(dot(normal, lightDirection), 0.0f);
// Specular
vec3 reflectionDirection = reflect(-lightDirection, normal);
float specular = pow(max(dot(viewDirection, reflectionDirection), 0.0f), 32);
lighting +=
(albedo * u_directionalLight[i].ambient) +
(albedo * diffuse * u_directionalLight[i].diffuse) +
(specular * u_directionalLight[i].specular);
}
// Loop through all point lights
// TODO
// vec3 lightDirection = normalize(lightPosition - position);
// float diffuse = max(dot(normal, lightDirection), 0.0f);
// lighting += diffuse * albedo * u_lightColor;
// Loop through all spot lights
// TODO
color = vec4(lighting, 1.0f);
}
+25
View File
@@ -0,0 +1,25 @@
#version 450 core
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in uint a_textureIndex;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat uint v_textureIndex;
layout(std140, binding = 0) uniform Camera
{
mat4 u_projectionView;
vec3 u_position;
};
void main()
{
v_color = a_color;
v_textureCoordinates = a_textureCoordinates;
v_textureIndex = a_textureIndex;
// Vclip = Model transform * Vlocal
gl_Position = vec4(a_position, 1.0f);
}
Binary file not shown.
+15
View File
@@ -0,0 +1,15 @@
# Blender 4.2.0
# www.blender.org
usemtl (null)
o Plane
v -1.000000 -1.000000 -0.000000
v 1.000000 -1.000000 -0.000000
v -1.000000 1.000000 0.000000
v 1.000000 1.000000 0.000000
vn -0.0000 -0.0000 1.0000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
s 0
f 1/1/1 2/2/1 4/3/1 3/4/1
+93 -27
View File
@@ -1,50 +1,116 @@
{
"init": "assets/lua/scene1-init.lua",
"camera": {
"name": "Camera",
"translate": [ 0.0, 0.0, 1.0 ],
"rotate": [ 0.0, 0.0, -1.0 ],
"scale": [ 1.0, 1.0, 1.0 ],
"type": "perspective",
"script": {
"type": "lua",
"name": "assets/lua/cameracontroller.lua"
"entities": [
{
"id": { "id": 12312312 },
"tag": { "tag": "Camera" },
"transform" : {
"translate": [0.0, 0.0, 1.0],
"rotate": [0.0, 0.0, -1.0],
"scale": [1.0, 1.0, 1.0]
},
"camera": { "type": "perspective" },
"lua-scripts": [
{ "path": "assets/lua/cameracontroller.lua" }
]
},
{
"id": { "id": 212563732 },
"tag": { "tag": "Skybox" },
"transform" : {
"translate": [0.0, 0.0, 0.0],
"rotate": [0.0, 0.0, 0.0],
"scale": [100.0, 100.0, 100.0]
},
"cubemap": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"texture": "assets/gfx/skybox.jpg"
}
},
"quad": [
{
"name": "Quad",
"id": { "id": 564564564 },
"tag": { "tag": "Quad" },
"transform" : {
"translate": [ 0.0, 0.0, 0.0 ],
"rotate": [ 0.0, 0.0, 0.0 ],
"scale": [ 1.0, 1.0, 1.0 ],
"scale": [ 1.0, 1.0, 1.0 ]
},
"model": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test.png"
}
},
{
"name": "Quad 2",
"translate": [ 1.1, 0.0, 0.0 ],
"id": { "id": 97897897 },
"tag": { "tag": "Quad 2" },
"transform" : {
"translate": [ 2.5, 0.0, 0.0 ],
"rotate": [ 0.0, 0.0, 0.0 ],
"scale": [ 1.0, 1.0, 1.0 ],
"scale": [ 1.0, 1.0, 1.0 ]
},
"model": {
"color": [ 0.5, 0.6, 0.8, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test.png"
}
},
{
"name": "Quad 3",
"translate": [ 2.2, 0.0, 0.0 ],
"rotate": [ 0.0, 0.0, 0.0 ],
"scale": [ 1.0, 1.0, 1.0 ],
"id": { "id": 3424242 },
"tag": { "tag": "Quad 3" },
"transform" : {
"translate": [ 5.0, 1.0, 0.0 ],
"rotate": [ 0.0, 0.0, -20.0 ],
"scale": [ 1.0, 1.0, 1.0 ]
},
"model": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test-inverted.png"
},
"children": [
{
"id": { "id": 4345472 },
"tag": { "tag": "Quad 4" },
"transform" : {
"translate": [ 1.7, 0.0, 0.0 ],
"rotate": [ 0.0, 0.0, 0.0 ],
"scale": [ 0.5, 0.5, 1.0 ]
},
"model": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test-inverted.png"
},
"children": [
{
"id": { "id": 5234723 },
"tag": { "tag": "Quad 5" },
"transform" : {
"translate": [ 2.0, 0.0, 0.0 ],
"rotate": [ 0.0, 0.0, -20.0 ],
"scale": [ 0.5, 0.5, 1.0 ]
},
"model": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test-inverted.png"
}
],
"text": [
}
]
}
]
},
{
"name": "Text",
"content": "HelloWorld!",
"font": "assets/fnt/dejavu-sans",
"font-size": 0,
"width": 150,
"lines": 3
"id": { "id": 675754 },
"tag": { "tag": "Text" },
"text": {
"content": "Hello World!",
"font": "assets/fnt/open-sans",
"font-size": 24,
"line-spacing": 1.0,
"width": 150
}
}
]
}
+1 -1
View File
@@ -3,7 +3,7 @@
"fullscreen": "windowed",
"height": 720,
"title": "Inferno",
"vsync": true,
"vsync": false,
"width": 1280
}
}
+9
View File
@@ -0,0 +1,9 @@
#+TITLE: Documentation
#+AUTHOR: Riyyi
#+LANGUAGE: en
#+OPTIONS: toc:nil
Topics:
- [[./shaders.org][Shaders]]
- [[./references.org][References]]
+326
View File
@@ -0,0 +1,326 @@
#+TITLE: Memory Layout
#+AUTHOR: Riyyi
#+LANGUAGE: en
#+OPTIONS: toc:nil
This chapter is about the memory layout of interface blocks in GLSL.
An interface block is a group of variables, a struct if you will.
There are 4 types of memory layouts that can be used, where the first two aren't
widely used as those are implementation dependent and require querying the
OpenGL API for memory offsets.
- packed
- shared
- std140
- std430
** std140
This type is usable in Uniform Buffer Objects (=UBO=) and Shader Storage Buffer
Objects (=SSBO=).
** std430
This type is only usable in Shader Storage Buffer Objects (=SSBO=).
Main points:
- Memory is organized into chunks.
- One chunk has 4 slots, 4 bytes per slot.
- Can't fit? Move to next chunk.
- An interface block is at least the size of 1 chunk.
The rules:
- Scalar =bool=, =int=, =uint=, =float=, and =double=
#+BEGIN_SRC
Both the size and alignment are the size of the scalar in basic machine types
(e.g., sizeof(GLfloat))
#+END_SRC
- Two-componment Vectors (e.g., =ivec2=)
#+BEGIN_SRC
Both the size and alignment are twice the size of the underlying scalar type.
#+END_SRC
- Three-component Vectors (e.g., =vec3=) and Four-component Vectors (e.g., =vec4=)
#+BEGIN_SRC
Both the size and alignment are four times the size of the scalar type. However,
this is only true when the member is part of an array or nested structure
#+END_SRC
- Array of Scalars and Vectors
#+BEGIN_SRC
The size of each element in the array will be the same size of the element type,
where three-component vectors are rounded up to the size four-component
vectors. This is also the array's alignment. The array's size will be the
element size times the number of elements.
#+END_SRC
- Column-major matrix or an array of column-major matrices of size C columns and R rows
#+BEGIN_SRC
Same layout as an array of N vectors each with R components, where N is the
total number of columns present.
#+END_SRC
- Row-major matrix or an array of row-major matrices of size R rows and C columns
#+BEGIN_SRC
Same layout as an array of N vectors each with C components, where N is the
total number of rows present.
#+END_SRC
Both =GLSL= and the =GLM= math library we're using have column-major matrices!
- Single-structure definition or an array of structures
#+BEGIN_SRC
Structure alignment is the same as the alignment of the biggest structure
member, where three-component vectors are rounded up to the size of
four-component vectors. Each structure will start on this alignment, and its
size will be the space neeeded by its members, according to the previous rules,
rounded up to a multiple of the structure alignment.
#+END_SRC
All of the examples described in the following segments are verified by querying
the OpenGL API.
*** Scalars
These types take up 1 (or 2 with =double=) slot and can appear after anything.
In the example below you can see that due to the larger alignment, the ~double~
is forced to the next chunk.
#+BEGIN_SRC glsl
// Var Size Alignment Offset
bool a; // 4 4 0
int b; // 4 4 4
uint c; // 4 4 8
double d; // 8 8 16
float e; // 4 4 24
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][b][c][ ] #1
[d][d][e][ ] #2
#+END_SRC
*** Two-component Vectors
**** Float
A Vec2 takes up 2 slots, so will be in the first or last half of a chunk.
#+BEGIN_SRC glsl
// Var Size Alignment Offset
vec2 a; // 8 8 0
float b; // 4 4 8
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][a][b][ ] #1
#+END_SRC
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float a; // 4 4 0
vec2 b; // 8 8 8
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][b][b] #1
#+END_SRC
**** Double
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float a; // 4 4 0
dvec2 b; // 16 16 16
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][ ][ ] #1
[b][b][b][b] #2
#+END_SRC
*** Three-component and Four-component Vectors
A Vec3 takes up 3 slots, the alignment is 4 slots so only fits at the start of a
chunk.
#+BEGIN_SRC glsl
// Var Size Alignment Offset
vec3 a; // 12 16 0
float b; // 4 4 12
vec4 c; // 16 16 16
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][a][a][b] #1
[c][c][c][c] #2
#+END_SRC
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float a; // 4 4 0
vec3 b; // 12 16 16
vec4 c; // 16 16 32
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][ ][ ] #1
[b][b][b][ ] #2
[c][c][c][c] #3
#+END_SRC
*** Array of Scalars and Vectors
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float[3] a; // 12 4 0
float b; // 4 4 12
float c; // 4 4 16
float[3] d; // 12 4 20
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][a][a][b] #1
[c][d][d][d] #1
#+END_SRC
*Note* the optimizations in the alignment and strides are not applicable to
~vec3~ elements, these remain unchanged from =std140=.
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float a; // 4 4 0
vec3[3] b; // 48 16 16
float c; // 4 4 64
float d; // 4 4 68
vec2[2] e; // 16 8 72
float f; // 4 4 88
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][ ][ ] #1, offset: 0
[b][b][b][ ] #2, offset: 16
[b][b][b][ ] #3, offset: 32
[b][b][b][ ] #4, offset: 48
[c][d][e][e] #5, offset: 64
[e][e][f][ ] #6, offset: 80
#+END_SRC
*Note* the offset needs to be a multiple of the alignment, forcing an entire
empty chunk in the example below.
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float a; // 4 4 0
dvec2[2] b; // 32 16 16
dvec3[2] c; // 64 32 64
float d; // 4 4 128
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][ ][ ] #1, offset: 0
[d][d][d][d] #2, offset: 16
[d][d][d][d] #3, offset: 32
[ ][ ][ ][ ] #4, offset: 48
[c][c][c][c] #5, offset: 64
[c][c][ ][ ] #6, offset: 80
[c][c][c][c] #7, offset: 96
[c][c][ ][ ] #8, offset: 112
[d][ ][ ][ ] #9, offset: 128
#+END_SRC
*** Matrices
Alignment is the same as an array of 1 “row” of the matrix.
No padding between the “rows” of a matrix, but will pad at the end.
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float a; // 4 4 0
mat2 b; // 16 8 8
vec2 c; // 4 4 24
float d; // 4 4 32
mat2[2] e; // 32 8 40
float f; // 4 4 72
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][b][b] #1, offset: 0
[b][b][c][c] #2, offset: 16
[d][ ][e][e] #3, offset: 32
[e][e][e][e] #4, offset: 48
[e][e][f][ ] #5, offset: 64
#+END_SRC
TODO: Add more examples
*** Structs
Alignment same as biggest struct member. Size is the size of all members,
rounded up to a multiple of the alignment.
In the example below you can see that the ~Stuff~ struct, including padding
between members, is 20 bytes in size. To make that a multiple of the alignment
additional padding needs to be put at the end, to make the total size 24 bytes.
Each element in the array of structs will apply the alignment again, as seen
with ~Stuff[1].a~.
#+BEGIN_SRC glsl
struct Stuff {
float a;
vec2 b;
float c;
};
// Var Size Alignment Offset
Stuff a; // 20 8
a.a; // 4 4 0
a.b; // 8 8 8
a.c; // 4 4 16
float b; // 4 4 24
Stuff[2] c; // 44 8
c.a; // 4 4 32
c.b; // 8 8 40
c.c; // 4 4 48
float d; // 4 4 80
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][a][a] #1, offset: 0
[a][ ][b][ ] #2, offset: 16
[c][ ][c][c] #3, offset: 32
[c][ ][c][ ] #4, offset: 48
[c][c][c][ ] #5, offset: 64
[d][ ][ ][ ] #6, offset: 80
#+END_SRC
TODO: Add more examples
* References
- https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL
- https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout
- [[https://www.oreilly.com/library/view/opengl-programming-guide/9780132748445/app09lev1sec2.html][The std140 Layout Rules]]
- [[https://www.youtube.com/watch?v=JPvbRko9lBg][(YouTube) WebGL 2: Uniform Buffer Objects]]
+1
View File
@@ -0,0 +1 @@
+8
View File
@@ -0,0 +1,8 @@
#+TITLE: Shaders
#+AUTHOR: Riyyi
#+LANGUAGE: en
#+OPTIONS: toc:nil
Topics:
- [[./memory-layout.org][Memory Layout]]
+4 -5
View File
@@ -13,10 +13,9 @@ file(GLOB_RECURSE GAME_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_executable(${GAME} ${GAME_SOURCES})
target_include_directories(${GAME} PRIVATE
"src"
"${CMAKE_SOURCE_DIR}/src"
"${CMAKE_SOURCE_DIR}/vendor/ruc/src")
"src")
target_link_libraries(${GAME} ${ENGINE})
target_compile_options(${GAME} PRIVATE ${COMPILE_FLAGS_PROJECT})
target_precompile_headers(${GAME} REUSE_FROM ${ENGINE})
@@ -25,5 +24,5 @@ target_precompile_headers(${GAME} REUSE_FROM ${ENGINE})
# Add 'make run' target
add_custom_target(run
COMMAND ${GAME}
WORKING_DIRECTORY ".."
)
WORKING_DIRECTORY "..")
add_dependencies(run ${ENGINE})
@@ -10,9 +10,10 @@
#include "inferno/component/transformcomponent.h"
#include "inferno/io/input.h"
#include "inferno/keycodes.h"
#include "inferno/script/cameracontroller.h"
namespace Inferno {
#include "cameracontroller.h"
namespace example {
void CameraController::updateOrthographic(float deltaTime)
{
@@ -20,10 +21,10 @@ void CameraController::updateOrthographic(float deltaTime)
float cameraRotateSpeed = ROTATE_SPEED * deltaTime;
if (Input::isKeyPressed(keyCode("GLFW_KEY_Q"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_Q"))) {
transform->rotate.z -= cameraRotateSpeed;
}
if (Input::isKeyPressed(keyCode("GLFW_KEY_E"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_E"))) {
transform->rotate.z += cameraRotateSpeed;
}
@@ -39,19 +40,19 @@ void CameraController::updateOrthographic(float deltaTime)
float cameraTranslateSpeed = TRANSLATE_SPEED * deltaTime;
// WASD movement
if (Input::isKeyPressed(keyCode("GLFW_KEY_W"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_W"))) {
transform->translate.x += -sin(glm::radians(transform->rotate.z)) * cameraTranslateSpeed;
transform->translate.y += cos(glm::radians(transform->rotate.z)) * cameraTranslateSpeed;
}
if (Input::isKeyPressed(keyCode("GLFW_KEY_S"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_S"))) {
transform->translate.x -= -sin(glm::radians(transform->rotate.z)) * cameraTranslateSpeed;
transform->translate.y -= cos(glm::radians(transform->rotate.z)) * cameraTranslateSpeed;
}
if (Input::isKeyPressed(keyCode("GLFW_KEY_A"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_A"))) {
transform->translate.x -= cos(glm::radians(transform->rotate.z)) * cameraTranslateSpeed;
transform->translate.y -= sin(glm::radians(transform->rotate.z)) * cameraTranslateSpeed;
}
if (Input::isKeyPressed(keyCode("GLFW_KEY_D"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_D"))) {
transform->translate.x += cos(glm::radians(transform->rotate.z)) * cameraTranslateSpeed;
transform->translate.y += sin(glm::radians(transform->rotate.z)) * cameraTranslateSpeed;
}
@@ -60,10 +61,10 @@ void CameraController::updateOrthographic(float deltaTime)
float zoomSpeed = ZOOM_SENSITIVITY * deltaTime;
if (Input::isKeyPressed(keyCode("GLFW_KEY_EQUAL"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_EQUAL"))) {
m_camera->zoomLevel -= zoomSpeed;
}
if (Input::isKeyPressed(keyCode("GLFW_KEY_MINUS"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_MINUS"))) {
m_camera->zoomLevel += zoomSpeed;
}
m_camera->zoomLevel = std::max(m_camera->zoomLevel, 0.25f);
@@ -73,8 +74,8 @@ void CameraController::updateOrthographic(float deltaTime)
void CameraController::updatePerspective(float deltaTime)
{
// Get mouse movement offset compared to last frame
float xOffset = Input::getXOffset() * MOUSE_SENSITIVITY;
float yOffset = Input::getYOffset() * MOUSE_SENSITIVITY;
float xOffset = Inferno::Input::getXOffset() * MOUSE_SENSITIVITY;
float yOffset = Inferno::Input::getYOffset() * MOUSE_SENSITIVITY;
m_camera->yaw += xOffset;
m_camera->pitch += yOffset;
@@ -107,24 +108,25 @@ void CameraController::updatePerspective(float deltaTime)
float cameraSpeed = TRANSLATE_SPEED * deltaTime;
// WASD movement
if (Input::isKeyPressed(keyCode("GLFW_KEY_W"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_W"))) {
transform->translate += transform->rotate * cameraSpeed;
}
if (Input::isKeyPressed(keyCode("GLFW_KEY_S"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_S"))) {
transform->translate -= transform->rotate * cameraSpeed;
}
if (Input::isKeyPressed(keyCode("GLFW_KEY_A"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_A"))) {
transform->translate -= glm::normalize(glm::cross(transform->rotate, m_camera->up)) * cameraSpeed;
}
if (Input::isKeyPressed(keyCode("GLFW_KEY_D"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_D"))) {
transform->translate += glm::normalize(glm::cross(transform->rotate, m_camera->up)) * cameraSpeed;
}
// Up / down movement
if (Input::isKeyPressed(keyCode("GLFW_KEY_SPACE"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_SPACE"))) {
transform->translate.y += cameraSpeed;
}
if (Input::isKeyPressed(keyCode("GLFW_KEY_LEFT_SHIFT"))) {
if (Inferno::Input::isKeyPressed(Inferno::keyCode("GLFW_KEY_LEFT_SHIFT"))) {
transform->translate.y -= cameraSpeed;
}
}
} // namespace Inferno
} // namespace example
@@ -14,20 +14,18 @@
#define ZOOM_SENSITIVITY 2.5f
#define MOUSE_SENSITIVITY 0.25f
namespace Inferno {
namespace example {
struct CameraComponent;
class CameraController final : public NativeScript {
class CameraController final : public Inferno::NativeScript {
public:
virtual void update(float deltaTime) override
{
m_camera = &getComponent<CameraComponent>();
m_camera = &getComponent<Inferno::CameraComponent>();
if (m_camera->type == CameraType::Orthographic) {
if (m_camera->type == Inferno::CameraType::Orthographic) {
updateOrthographic(deltaTime);
}
else if (m_camera->type == CameraType::Perspective) {
else if (m_camera->type == Inferno::CameraType::Perspective) {
updatePerspective(deltaTime);
}
}
@@ -36,7 +34,9 @@ public:
void updatePerspective(float deltaTime);
private:
CameraComponent* m_camera { nullptr };
Inferno::CameraComponent* m_camera { nullptr };
};
} // namespace Inferno
BIND_NATIVE(CameraController);
} // namespace example
+2 -4
View File
@@ -1,18 +1,16 @@
file(GLOB_RECURSE ENGINE_SOURCES "${ENGINE}/*.cpp")
add_library(${ENGINE} ${ENGINE_SOURCES})
target_include_directories(${ENGINE} PRIVATE
target_include_directories(${ENGINE} PUBLIC
"."
"../vendor/entt/src"
"../vendor/glad/include"
"../vendor/glfw/include"
"../vendor/glm"
"../vendor/json/include"
"../vendor/lua"
"../vendor/ruc/src"
"../vendor/sol2/include"
"../vendor/stb")
target_link_libraries(${ENGINE} ${ENGINE}-dependencies)
target_compile_options(${ENGINE} PRIVATE ${COMPILE_FLAGS_PROJECT})
# ------------------------------------------
+36 -25
View File
@@ -1,29 +1,35 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <utility> // std::pair
#include "glm/ext/vector_float3.hpp"
#include "glm/gtc/type_ptr.hpp" // glm::make_mat4
#include "ruc/format/log.h"
#include "ruc/meta/assert.h"
#include "inferno/application.h"
#include "inferno/component/transformcomponent.h"
#include "inferno/core.h"
#include "inferno/event/applicationevent.h"
#include "inferno/event/event.h"
#include "inferno/event/keyevent.h"
#include "inferno/event/mouseevent.h"
// #include "inferno/io/gltffile.h"
#include "inferno/io/input.h"
#include "inferno/keycodes.h"
#include "inferno/render/buffer.h"
#include "inferno/render/context.h"
#include "inferno/render/font.h"
#include "inferno/render/framebuffer.h"
#include "inferno/render/uniformbuffer.h"
#include "inferno/system/rendersystem.h"
// #include "inferno/render/gltf.h"
#include "inferno/asset/shader.h"
#include "inferno/asset/texture.h"
#include "inferno/render/render-command.h"
#include "inferno/render/renderer.h"
#include "inferno/render/shader.h"
#include "inferno/render/texture.h"
#include "inferno/scene/scene.h"
#include "inferno/settings.h"
#include "inferno/time.h"
@@ -48,12 +54,11 @@ Application::Application()
Input::initialize();
RenderCommand::initialize();
RenderSystem::the().initialize(m_window->getWidth(), m_window->getHeight());
m_scene = std::make_shared<Scene>();
m_scene->initialize();
// Load assets
// m_font = FontManager::the().load("assets/fnt/dejavu-sans");
// auto bla = GlTFFile::read("assets/gltf/box.glb");
@@ -74,13 +79,16 @@ Application::Application()
Application::~Application()
{
m_scene->destroy();
Uniformbuffer::destroy();
FontManager::destroy();
RendererCharacter::destroy();
RendererFont::destroy();
Renderer2D::destroy();
Renderer3D::destroy();
RendererCubemap::destroy();
RendererPostProcess::destroy();
RendererLightCube::destroy();
RenderCommand::destroy();
TextureManager::destroy();
ShaderManager::destroy();
AssetManager::destroy();
// Input::destroy();
Settings::destroy();
@@ -142,13 +150,20 @@ int Application::run()
// offset
#endif
double gametime = 0;
uint64_t frames = 0;
while (!m_window->shouldClose()) {
float time = Time::time();
float deltaTime = time - m_lastFrameTime;
m_lastFrameTime = time;
// ruc::debug("Frametime " << deltaTime * 1000 << "ms");
// ruc::debug("Frametime {}ms", deltaTime * 1000);
gametime += deltaTime;
frames++;
// ---------------------------------
// Update
update();
@@ -157,27 +172,18 @@ int Application::run()
m_window->update();
m_scene->update(deltaTime);
// ---------------------------------
// Render
render();
RenderCommand::clearColor({ 0.2f, 0.3f, 0.3f, 1.0f });
RenderCommand::clear();
Renderer2D::the().beginScene(m_scene->cameraProjectionView()); // camera, lights, environment
RendererCharacter::the().beginScene();
m_scene->render();
// RendererCharacter::the().drawCharacter(character, f->texture());
Renderer2D::the().endScene();
RendererCharacter::the().endScene();
m_window->render();
}
ruc::debug("Application shutdown");
ruc::debug("Average frametime: {:.2f}ms", (gametime / frames) * 1000);
return m_status;
}
@@ -206,7 +212,7 @@ bool Application::onWindowResize(WindowResizeEvent& e)
{
ruc::info("WindowResizeEvent {}x{}", e.getWidth(), e.getHeight());
RenderCommand::setViewport(0, 0, e.getWidth(), e.getHeight());
RenderSystem::the().resize(e.getWidth(), e.getHeight());
return true;
}
@@ -220,6 +226,11 @@ bool Application::onKeyPress(KeyPressEvent& e)
m_window->setShouldClose(true);
}
if (e.getKey() == keyCode("GLFW_KEY_F12")) {
ruc::info("Taking screenshot..");
Texture::saveScreenshotPNG("screenshot.png", m_window->getWidth(), m_window->getHeight());
}
return true;
}
+6 -5
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
@@ -8,18 +8,18 @@
#include <memory> // std::unique_ptr, std::shared_ptr
#include "ruc/singleton.h"
namespace Inferno {
class Event;
class Font;
class Framebuffer;
class KeyPressEvent;
class MousePositionEvent;
class Scene;
class Window;
class WindowCloseEvent;
class WindowResizeEvent;
struct TransformComponent;
class Application {
public:
@@ -36,9 +36,10 @@ public:
virtual bool onKeyPress(KeyPressEvent& e);
virtual bool onMousePosition(MousePositionEvent& e);
inline void setStatus(int status) { m_status = status; }
void setStatus(int status) { m_status = status; }
inline Window& window() const { return *m_window; }
Scene& scene() const { return *m_scene; }
Window& window() const { return *m_window; }
static Application& the() { return *s_instance; }
+59
View File
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <memory> // std::shared_ptr, std::static_pointer_cast
#include <string>
#include <string_view>
#include "ruc/format/log.h"
#include "inferno/asset/asset-manager.h"
#include "inferno/asset/shader.h"
namespace Inferno {
// -----------------------------------------
AssetManager::AssetManager(s)
{
ruc::info("AssetManager initialized");
}
AssetManager::~AssetManager()
{
}
void AssetManager::add(std::string_view path, std::shared_ptr<Asset> asset)
{
// Construct (key, value) pair and insert it into the unordered_map
auto stringPath = std::string(path.begin(), path.end());
m_assetList.emplace(std::move(stringPath), std::move(asset));
}
bool AssetManager::exists(std::string_view path)
{
return m_assetList.find(path.data()) != m_assetList.end();
}
std::nullptr_t AssetManager::remove(std::string_view path)
{
if (exists(path)) {
m_assetList.erase(path.data());
}
return nullptr;
}
std::nullptr_t AssetManager::remove(std::shared_ptr<Asset> asset)
{
if (exists(asset->path())) {
m_assetList.erase(asset->path());
}
return nullptr;
}
} // namespace Inferno
+98
View File
@@ -0,0 +1,98 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstddef> // std::nullptr_t
#include <memory> // std::shared_ptr
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility> // std::forward
#include "ruc/meta/assert.h"
#include "ruc/meta/types.h"
#include "ruc/singleton.h"
namespace Inferno {
class Asset {
public:
virtual ~Asset() = default;
std::string path() const { return m_path; }
// -------------------------------------
std::string className() const { return typeid(*this).name(); }
template<typename T>
bool fastIs() const = delete;
virtual bool isFont() const { return false; }
virtual bool isModel() const { return false; }
virtual bool isShader() const { return false; }
virtual bool isTexture() const { return false; }
virtual bool isTexture2D() const { return false; }
virtual bool isTextureCubemap() const { return false; }
virtual bool isTextureFramebuffer() const { return false; }
protected:
Asset(std::string_view path)
: m_path(std::string(path.begin(), path.end()))
{
}
protected:
std::string m_path;
};
// -----------------------------------------
template<typename T>
concept IsAsset = std::same_as<Asset, T> || std::derived_from<T, Asset>;
// -----------------------------------------
class AssetManager : public ruc::Singleton<AssetManager> {
public:
AssetManager(s);
virtual ~AssetManager();
void add(std::string_view path, std::shared_ptr<Asset> asset);
bool exists(std::string_view path);
std::nullptr_t remove(std::string_view path);
std::nullptr_t remove(std::shared_ptr<Asset> asset);
template<IsAsset T>
std::shared_ptr<T> get(std::string_view path)
{
if (!exists(path)) {
return nullptr;
}
auto asset = m_assetList.at(path.data());
VERIFY(is<T>(asset.get()), "expected asset {}, got {}", typeid(T).name(), asset->className());
return std::static_pointer_cast<T>(asset);
}
template<IsAsset T, typename... Args>
std::shared_ptr<T> load(std::string_view path, Args&&... args)
{
if (exists(path)) {
return get<T>(path);
}
auto asset = T::create(path, std::forward<Args>(args)...);
add(path, asset);
return asset;
}
private:
std::unordered_map<std::string, std::shared_ptr<Asset>> m_assetList;
};
} // namespace Inferno
+175
View File
@@ -0,0 +1,175 @@
/*
* Copyright (C) 2022-2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <array> // std::array
#include <charconv> // std;:from_chars
#include <cstdint> // int32_t, uint32_t
#include <ranges> // std::views::split
#include <string> // std::getline
#include "ruc/file.h"
#include "ruc/meta/assert.h"
#include "ruc/meta/concepts.h"
#include "inferno/asset/font.h"
#include "inferno/asset/texture.h"
namespace Inferno {
std::shared_ptr<Font> Font::create(std::string_view path)
{
auto result = std::shared_ptr<Font>(new Font(path));
auto stringPath = std::string(path);
std::string file = stringPath + ".fnt";
std::string image = stringPath + ".png";
std::string font = ruc::File(file).data();
result->parseFont(font);
result->m_texture = Texture2D::create(image);
return result;
}
// TODO: Move this to ruc
template<Integral T>
static T convert(std::string_view value)
{
T tmp;
std::from_chars(value.data(), value.data() + value.size(), tmp);
return tmp;
}
void Font::parseFont(const std::string& font)
{
std::istringstream iss(font);
for (std::string line; std::getline(iss, line);) {
std::string action = findAction(line);
const std::vector<std::string> columns = findColumns(line);
// Info
// ---------------------------------
if (action.compare("info") == 0) {
m_size = convert<unsigned char>(findValue("size", columns));
auto paddings = findValue("padding", columns) | std::views::split(',');
size_t i = 0;
for (const auto& padding : paddings) {
// top, right, bottom, left
m_padding[i++] = convert<uint32_t>(padding.data()) - PADDING;
}
continue;
}
// Common
// ---------------------------------
if (action.compare("common") == 0) {
m_lineSpacing = convert<uint32_t>(findValue("lineHeight", columns)) - m_padding[Padding::Top] - m_padding[Padding::Bottom];
continue;
}
// Symbol
// ---------------------------------
if (action.compare("char") == 0) {
auto id = convert<char>(findValue("id", columns));
auto width = convert<uint32_t>(findValue("width", columns));
auto height = convert<uint32_t>(findValue("height", columns));
Symbol symbol = {
.id = id,
.position = {
convert<uint32_t>(findValue("x", columns)) + m_padding[Padding::Left],
convert<uint32_t>(findValue("y", columns)) + m_padding[Padding::Top],
},
.size = {
width == 0 ? 0 : width - m_padding[Padding::Left] - m_padding[Padding::Right],
height == 0 ? 0 : height - m_padding[Padding::Top] - m_padding[Padding::Bottom],
},
.offset = {
convert<int32_t>(findValue("xoffset", columns)) + m_padding[Padding::Left],
convert<int32_t>(findValue("yoffset", columns)) + m_padding[Padding::Top],
},
.advance = convert<uint32_t>(findValue("xadvance", columns)) - m_padding[Padding::Left] - m_padding[Padding::Right]
};
m_symbolList.emplace(id, std::make_shared<Symbol>(symbol));
continue;
}
// Kerning
// ---------------------------------
if (action.compare("kerning") == 0) {
unsigned char first = convert<unsigned char>(findValue("first", columns));
unsigned char second = convert<unsigned char>(findValue("second", columns));
char amount = convert<char>(findValue("amount", columns));
// Add the kerning of the previous symbol to this symbol
if (m_symbolList.find(second) != m_symbolList.end()) {
auto symbol = m_symbolList.at(second);
symbol->kernings.emplace(first, amount);
}
continue;
}
}
}
std::string Font::findAction(const std::string& line) const
{
return line.substr(0, line.find(" "));
}
std::vector<std::string> Font::findColumns(const std::string& line) const
{
std::vector<std::string> elements;
size_t index = 0;
size_t find = 0;
size_t findFirstNotOf = 0;
// Loop over line symbols
while (find != std::string::npos) {
find = line.find(" ", index);
findFirstNotOf = line.find_first_not_of(" ", index);
// Add element
if (find > findFirstNotOf) {
elements.push_back(line.substr(index, find - findFirstNotOf));
index += 1 + find - findFirstNotOf;
}
// Skip double spaces
else {
index = findFirstNotOf;
}
}
VERIFY(!elements.empty(), "Font file did not find any columns");
return elements;
}
std::string Font::findValue(const std::string& key, const std::vector<std::string>& columns) const
{
size_t find = 0;
// Loop over columns
for (auto& column : columns) {
find = column.find(key + "=");
if (find != std::string::npos) {
return column.substr(key.length() + 1);
}
}
VERIFY(false, "Font file did not contain key '{}'", key);
return "";
}
} // namespace Inferno
void ruc::format::Formatter<glm::ivec2>::format(Builder& builder, glm::ivec2 value) const
{
return Formatter<std::vector<int32_t>>::format(builder, { value.x, value.y });
}
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright (C) 2022-2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <array> // std::array
#include <cstdint> // int32_t, uint32_t
#include <memory> // std::shared_ptr
#include <string> // std::string
#include <string_view>
#include <unordered_map> // std::unordered_map
#include <vector> // std::vector
#include "glm/ext/vector_int2.hpp" // glm::ivec2
#include "glm/ext/vector_uint2.hpp" // glm::uvec2
#include "ruc/format/format.h"
#include "inferno/asset/asset-manager.h"
#define PADDING 3
namespace Inferno {
class Texture;
struct Symbol {
char id; // Symbol
glm::uvec2 position; // Position
glm::uvec2 size; // Width/height
glm::ivec2 offset; // Offset from baseline to left / top of glyph
uint32_t advance; // Amount to advance to next glyph
std::unordered_map<unsigned char, char> kernings; // Kernings for symbols that come before this one
};
// -------------------------------------
class Font final : public Asset {
public:
virtual ~Font() {}
// Factory function
static std::shared_ptr<Font> create(std::string_view path);
enum Padding {
Top = 0,
Right,
Bottom,
Left,
};
inline uint32_t size() const { return m_size; }
inline uint32_t lineSpacing() const { return m_lineSpacing; }
inline std::shared_ptr<Texture> texture() const { return m_texture; }
inline std::shared_ptr<Symbol> get(unsigned char c) const { return m_symbolList.at(c); }
inline std::shared_ptr<Symbol> operator[](unsigned char c) const { return m_symbolList.at(c); }
private:
Font(std::string_view path)
: Asset(path)
{
}
void parseFont(const std::string& font);
std::string findAction(const std::string& line) const;
std::vector<std::string> findColumns(const std::string& line) const;
std::string findValue(const std::string& key, const std::vector<std::string>& columns) const;
virtual bool isFont() const override { return true; }
private:
unsigned char m_size = { 0 };
uint32_t m_lineSpacing = { 0 };
std::array<uint32_t, 4> m_padding = { 0 };
std::shared_ptr<Texture> m_texture;
std::unordered_map<unsigned char, std::shared_ptr<Symbol>> m_symbolList;
};
// -----------------------------------------
// clang-format off
template<>
inline bool Asset::fastIs<Font>() const { return isFont(); }
// clang-format on
} // namespace Inferno
template<>
struct ruc::format::Formatter<glm::ivec2> : Formatter<std::vector<int32_t>> {
void format(Builder& builder, glm::ivec2 value) const;
};
// FontManager fm;
// Font f = fm.load("path/to/font");
// Font f2("path/to/font");
// Symbol c = f['a'];
// Look into using signed distance fields for texture map generation ? anti-aliasing for text
// https://youtu.be/d8cfgcJR9Tk
+110
View File
@@ -0,0 +1,110 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstdint> // uint32_t
#include <memory> // std::shared_ptr
#include "assimp/Importer.hpp"
#include "assimp/mesh.h"
#include "assimp/postprocess.h"
#include "assimp/scene.h"
#include "assimp/texture.h"
#include "inferno/asset/model.h"
#include "inferno/asset/texture.h"
namespace Inferno {
std::shared_ptr<Model> Model::create(std::string_view path)
{
auto result = std::shared_ptr<Model>(new Model(path));
Assimp::Importer importer; // importer destructor uses RAII cleanup
importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE);
const aiScene* scene = importer.ReadFile(
path.data(),
aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType);
VERIFY(scene != nullptr && (scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) == 0 && scene->mRootNode != nullptr,
"assimp loading file failed: {}", importer.GetErrorString());
processScene(result, scene);
processNode(result, scene->mRootNode, scene);
return result;
}
void Model::processScene(std::shared_ptr<Model> model, const aiScene* scene)
{
VERIFY(scene->HasMeshes(), "malformed model");
VERIFY(scene->mNumTextures < 2, "unsupported model type: {}/1", scene->mNumTextures);
if (scene->mNumTextures == 1) {
aiTexture* texture = scene->mTextures[0];
model->m_texture = Texture2D::create(texture);
}
}
void Model::processNode(std::shared_ptr<Model> model, aiNode* node, const aiScene* scene)
{
for (uint32_t i = 0; i < node->mNumMeshes; ++i) {
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
processMesh(model, mesh, scene, node->mTransformation);
}
for (uint32_t i = 0; i < node->mNumChildren; ++i) {
processNode(model, node->mChildren[i], scene);
}
}
void Model::processMesh(std::shared_ptr<Model> model, aiMesh* mesh, const aiScene* scene, aiMatrix4x4 parentTransform)
{
VERIFY(mesh->HasPositions(), "malformed model");
VERIFY(mesh->HasNormals(), "malformed model");
// Pre-allocate memory
size_t startIndex = model->m_vertices.size();
model->m_vertices.resize(startIndex + mesh->mNumVertices);
for (uint32_t i = 0; i < mesh->mNumVertices; ++i) {
aiVector3D v = parentTransform * mesh->mVertices[i];
model->m_vertices[startIndex + i].position = { v.x, v.y, v.z };
}
// Size of vertices == size of normals
for (uint32_t i = 0; i < mesh->mNumVertices; ++i) {
aiVector3D normal = mesh->mNormals[startIndex + i];
model->m_vertices[startIndex + i].normal = { normal.x, normal.y, normal.z };
}
if (mesh->HasTextureCoords(0)) {
// Size of vertices == size of texture coordinates
for (uint32_t i = 0; i < mesh->mNumVertices; ++i) {
aiVector3D tc = mesh->mTextureCoords[0][i];
model->m_vertices[startIndex + i].textureCoordinates = { tc.x, tc.y };
}
}
// TODO: position in the texture atlas
if (mesh->HasFaces()) {
// Pre-allocate memory
size_t startIndex2 = model->m_elements.size();
model->m_elements.resize(startIndex2 + (mesh->mNumFaces * 3)); // assuming triangles
size_t offset = 0;
for (uint32_t i = 0; i < mesh->mNumFaces; ++i) {
aiFace face = mesh->mFaces[i];
VERIFY(face.mNumIndices == 3, "unsupported face type: {}", face.mNumIndices);
for (uint32_t j = 0; j < face.mNumIndices; ++j, ++offset) {
// Indices are referenced relative to vertices[0], if there are multiple meshes,
// then the indices need to be offset by the total amount of vertices
model->m_elements[startIndex2 + (i * 3) + j] = startIndex + face.mIndices[j];
}
}
}
}
} // namespace Inferno
+60
View File
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstdint> // uint32_t
#include <memory>
#include <span>
#include <vector>
#include "assimp/scene.h"
#include "inferno/asset/asset-manager.h"
#include "inferno/render/renderer.h"
namespace Inferno {
class Texture2D;
class Model final : public Asset {
public:
virtual ~Model() {}
// Factory function
static std::shared_ptr<Model> create(std::string_view path);
std::span<const Vertex> vertices() const { return m_vertices; }
std::span<const uint32_t> elements() const { return m_elements; }
std::shared_ptr<Texture2D> texture() const { return m_texture; }
private:
Model(std::string_view path)
: Asset(path)
{
}
static void processScene(std::shared_ptr<Model> model, const aiScene* scene);
static void processNode(std::shared_ptr<Model> model, aiNode* node, const aiScene* scene);
static void processMesh(std::shared_ptr<Model> model, aiMesh* mesh, const aiScene* scene, aiMatrix4x4 parentTransform = aiMatrix4x4());
virtual bool isModel() const override { return true; }
private:
std::vector<Vertex> m_vertices;
std::vector<uint32_t> m_elements;
// Some file formats embed their texture
std::shared_ptr<Texture2D> m_texture;
};
// clang-format off
template<>
inline bool Asset::fastIs<Model>() const { return isModel(); }
// clang-format on
} // namespace Inferno
// TODO: figure out this weird thing: https://github.com/assimp/assimp/blob/master/BUILDBINARIES_EXAMPLE.bat#L6-L10
+214
View File
@@ -0,0 +1,214 @@
/*
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <vector> // std::vector
#include "glad/glad.h"
#include "glm/gtc/type_ptr.hpp" // glm::value_ptr
#include "ruc/file.h"
#include "ruc/format/log.h"
#include "ruc/meta/assert.h"
#include "inferno/asset/shader.h"
namespace Inferno {
std::shared_ptr<Shader> Shader::create(std::string_view path)
{
auto result = std::shared_ptr<Shader>(new Shader(path));
// Get file contents
auto stringPath = std::string(path);
std::string vertexSrc = ruc::File(stringPath + ".vert").data();
std::string fragmentSrc = ruc::File(stringPath + ".frag").data();
// Compile shaders
uint32_t vertexID = result->compileShader(GL_VERTEX_SHADER, vertexSrc.c_str());
uint32_t fragmentID = result->compileShader(GL_FRAGMENT_SHADER, fragmentSrc.c_str());
// Link shaders
if (vertexID > 0 && fragmentID > 0) {
result->m_id = result->linkShader(vertexID, fragmentID);
}
// Clear resources
else if (vertexID > 0)
glDeleteShader(vertexID);
else if (fragmentID > 0)
glDeleteShader(fragmentID);
return result;
}
Shader::~Shader()
{
if (m_id > 0) {
glDeleteProgram(m_id);
m_id = 0;
}
}
// -----------------------------------------
uint32_t Shader::findUniformLocation(std::string_view name)
{
// Cache uniform locations, prevent going to the GPU every call
if (m_uniformLocations.find(name) != m_uniformLocations.end()) {
return m_uniformLocations[name];
}
int32_t location = glGetUniformLocation(m_id, name.data());
VERIFY(location != -1, "Shader could not find uniform '{}'", name);
m_uniformLocations[name] = static_cast<uint32_t>(location);
return location;
}
void Shader::setInt(std::string_view name, int value)
{
// Set uniform int
glUniform1i(findUniformLocation(name), value);
}
void Shader::setInt(std::string_view name, int* values, uint32_t count)
{
// Set uniform int array
glUniform1iv(findUniformLocation(name), count, values);
}
void Shader::setFloat(std::string_view name, float value)
{
// Set uniform float
glUniform1f(findUniformLocation(name), value);
}
void Shader::setFloat(std::string_view name, float v1, float v2, float v3, float v4)
{
// Set uniform vec4 data
glUniform4f(findUniformLocation(name), v1, v2, v3, v4);
}
void Shader::setFloat(std::string_view name, glm::vec2 value)
{
// Set uniform vec2 data
glUniform2f(findUniformLocation(name), value.x, value.y);
}
void Shader::setFloat(std::string_view name, glm::vec3 value)
{
// Set uniform vec3 data
glUniform3f(findUniformLocation(name), value.x, value.y, value.z);
}
void Shader::setFloat(std::string_view name, glm::vec4 value)
{
// Set uniform vec4 data
glUniform4f(findUniformLocation(name), value.x, value.y, value.z, value.w);
}
void Shader::setFloat(std::string_view name, glm::mat3 matrix)
{
// Set uniform mat3 data
glUniformMatrix3fv(findUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix));
}
void Shader::setFloat(std::string_view name, glm::mat4 matrix)
{
// Set uniform mat4 data
glUniformMatrix4fv(findUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix));
}
void Shader::bind() const
{
glUseProgram(m_id);
}
void Shader::unbind() const
{
glUseProgram(0);
}
// -----------------------------------------
uint32_t Shader::compileShader(int32_t type, const char* source) const
{
// Create new shader
uint32_t shader = 0;
shader = glCreateShader(type);
// Attach shader source to shader object
glShaderSource(shader, 1, &source, nullptr);
// Compile shader
glCompileShader(shader);
// Check compilation status
if (checkStatus(shader) == GL_TRUE) {
return shader;
}
// On fail
glDeleteShader(shader);
return 0;
}
uint32_t Shader::linkShader(uint32_t vertex, uint32_t fragment) const
{
// Create new shader program
uint32_t shaderProgram = 0;
shaderProgram = glCreateProgram();
// Attach both shaders to the shader program
glAttachShader(shaderProgram, vertex);
glAttachShader(shaderProgram, fragment);
// Setup vertex attributes
glBindAttribLocation(shaderProgram, 0, "a_position");
// Link the shaders
glLinkProgram(shaderProgram);
// Clear resources
glDeleteShader(vertex);
glDeleteShader(fragment);
// Check linking status
if (checkStatus(shaderProgram, true) == GL_TRUE) {
return shaderProgram;
}
// On fail
glDeleteProgram(shaderProgram);
return 0;
}
int32_t Shader::checkStatus(uint32_t check, bool isProgram) const
{
int32_t success;
int32_t maxLength = 0;
std::vector<char> infoLog;
// Get the compilation/linking status
!isProgram
? glGetShaderiv(check, GL_COMPILE_STATUS, &success)
: glGetProgramiv(check, GL_LINK_STATUS, &success);
if (success != GL_TRUE) {
// Get max length of the log including \0 terminator
!isProgram
? glGetShaderiv(check, GL_INFO_LOG_LENGTH, &maxLength)
: glGetProgramiv(check, GL_INFO_LOG_LENGTH, &maxLength);
// Reserve data for the log
infoLog.reserve(maxLength);
// Retrieve the error message
!isProgram
? glGetShaderInfoLog(check, maxLength, nullptr, &infoLog[0])
: glGetProgramInfoLog(check, maxLength, nullptr, &infoLog[0]);
ruc::warn("Shader {}", infoLog.data());
}
VERIFY(success == GL_TRUE, "Shader program creation failed!");
return success;
}
} // namespace Inferno
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstdint> // int32_t, uint32_t
#include <string_view>
#include <unordered_map>
#include "glm/fwd.hpp" // glm::mat3, glm::mat4, glm::vec2, glm::vec3
#include "inferno/asset/asset-manager.h"
namespace Inferno {
class Shader : public Asset {
public:
virtual ~Shader();
// Factory function
static std::shared_ptr<Shader> create(std::string_view path);
uint32_t findUniformLocation(std::string_view name);
void setInt(std::string_view name, int value);
void setInt(std::string_view name, int* values, uint32_t count);
void setFloat(std::string_view name, float value);
void setFloat(std::string_view name, float v1, float v2, float v3, float v4);
void setFloat(std::string_view name, glm::vec2 value);
void setFloat(std::string_view name, glm::vec3 value);
void setFloat(std::string_view name, glm::vec4 value);
void setFloat(std::string_view name, glm::mat3 matrix);
void setFloat(std::string_view name, glm::mat4 matrix);
void bind() const;
void unbind() const;
uint32_t id() const { return m_id; }
protected:
uint32_t compileShader(int32_t type, const char* shaderSource) const;
uint32_t linkShader(uint32_t vertex, uint32_t fragment) const;
int32_t checkStatus(uint32_t check, bool isProgram = false) const;
private:
Shader(std::string_view path)
: Asset(path)
{
}
virtual bool isShader() const override { return true; }
private:
uint32_t m_id { 0 };
std::unordered_map<std::string_view, uint32_t> m_uniformLocations;
};
// -----------------------------------------
// clang-format off
template<>
inline bool Asset::fastIs<Shader>() const { return isShader(); }
// clang-format on
} // namespace Inferno
+340
View File
@@ -0,0 +1,340 @@
/*
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <climits> // UINT_MAX
#include <cstdint> // uint8_t, uint32_t
#include <memory> // std::shared_ptr
#include "assimp/texture.h"
#include "glad/glad.h"
#include "ruc/meta/assert.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb/stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb/stb_image_write.h"
#include "inferno/asset/texture.h"
namespace Inferno {
Texture::~Texture()
{
glDeleteTextures(1, &m_id);
}
// -----------------------------------------
void Texture::savePNG(std::string_view path, std::shared_ptr<Texture> texture)
{
texture->bind();
// Allocate memory
uint32_t dataFormat = texture->m_dataFormat == GL_RGBA ? 4 : 3;
size_t dataSize = texture->m_width * texture->m_height * dataFormat;
std::vector<unsigned char> data(dataSize);
// Read texture data from the GPU
glGetTexImage(GL_TEXTURE_2D, 0, texture->m_dataFormat, texture->m_dataType, data.data());
// Write image to a file
stbi_flip_vertically_on_write(1);
stbi_write_png(
path.data(),
texture->m_width,
texture->m_height,
dataFormat,
data.data(),
texture->m_width * dataFormat);
texture->unbind();
}
void Texture::saveScreenshotPNG(std::string_view path, uint32_t width, uint32_t height)
{
// Allocate memory
size_t dataSize = width * height * 4;
std::vector<unsigned char> data(dataSize);
// Set the pack alignment to 1 (to avoid row alignment issues)
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Read the pixels from the default framebuffer
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
stbi_flip_vertically_on_write(1);
stbi_write_png(path.data(), width, height, 4, data.data(), width * 4);
}
// -----------------------------------------
void Texture::init(uint32_t width, uint32_t height, uint8_t channels)
{
init(width, height,
(channels == 3) ? GL_RGB8 : GL_RGBA8,
(channels == 3) ? GL_RGB : GL_RGBA,
GL_UNSIGNED_BYTE);
}
void Texture::init(uint32_t width, uint32_t height, uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType)
{
m_width = width;
m_height = height;
m_internalFormat = internalFormat;
m_dataFormat = dataFormat;
m_dataType = dataType;
}
// -----------------------------------------
std::shared_ptr<Texture2D> Texture2D::create(std::string_view path)
{
auto result = std::shared_ptr<Texture2D>(new Texture2D(path));
int width = 0;
int height = 0;
int channels = 0;
unsigned char* data = nullptr;
// Load image data
stbi_set_flip_vertically_on_load(1);
data = stbi_load(path.data(), &width, &height, &channels, STBI_default);
VERIFY(data, "failed to load image: '{}'", path);
result->init(width, height, channels);
result->createImpl(data);
// Clean resources
stbi_image_free(data);
return result;
}
std::shared_ptr<Texture2D> Texture2D::create(aiTexture* texture)
{
auto result = std::shared_ptr<Texture2D>(new Texture2D(texture->mFilename.C_Str()));
int width = static_cast<int>(texture->mWidth);
int height = static_cast<int>(texture->mHeight);
int channels = 0;
unsigned char* data = reinterpret_cast<unsigned char*>(texture->pcData);
bool isCompressed = height == 0; // height 0 is compression, byte length stored in width variable
if (isCompressed) {
stbi_set_flip_vertically_on_load(0);
data = stbi_load_from_memory(data, width, &width, &height, &channels, STBI_default);
}
else {
channels = 4; // ARGB888
// TODO: imprement format hints? `archFormatHint'
VERIFY_NOT_REACHED();
}
result->init(width, height, channels);
result->createImpl(data);
return result;
}
void Texture2D::bind(uint32_t unit) const
{
// Set active unit
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, m_id);
// Reset unit
glActiveTexture(GL_TEXTURE0);
}
void Texture2D::unbind() const
{
glBindTexture(GL_TEXTURE_2D, 0);
}
void Texture2D::createImpl(unsigned char* data)
{
m_id = UINT_MAX;
// Create texture object
glGenTextures(1, &m_id);
// Bind texture object
glBindTexture(GL_TEXTURE_2D, m_id);
// Set unpacking of pixel data to byte-alignment,
// this prevents alignment issues when using a single byte for color
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Generate texture
glTexImage2D(
GL_TEXTURE_2D, // Texture target
0, // Midmap level, base starts at level 0
m_internalFormat, // Texture format
m_width, m_height, // Image width/height
0, // Always 0 (legacy)
m_dataFormat, // Texture source format
m_dataType, // Texture source datatype
data); // Image data
// Set the texture wrapping / filtering options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Magnify
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Minify
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // X
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Y
// Automatically generate all mipmap levels
glGenerateMipmap(GL_TEXTURE_2D);
// Unbind texture object
glBindTexture(GL_TEXTURE_2D, 0);
}
// -----------------------------------------
std::shared_ptr<TextureCubemap> TextureCubemap::create(std::string_view path)
{
auto result = std::shared_ptr<TextureCubemap>(new TextureCubemap(path));
result->createImpl();
return result;
}
void TextureCubemap::bind(uint32_t unit) const
{
// Set active unit
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_id);
// Reset unit
glActiveTexture(GL_TEXTURE0);
}
void TextureCubemap::unbind() const
{
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
void TextureCubemap::createImpl()
{
m_id = UINT_MAX;
// Create texture object
glGenTextures(1, &m_id);
// Bind texture object
glBindTexture(GL_TEXTURE_CUBE_MAP, m_id);
// Set unpacking of pixel data to byte-alignment,
// this prevents alignment issues when using a single byte for color
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
stbi_set_flip_vertically_on_load(0);
static constexpr const char* cubemapPaths[6] { "-px", "-nx", "-py", "-ny", "-pz", "-nz" };
size_t dotIndex = m_path.find_last_of('.');
std::string path = m_path.substr(0, dotIndex);
std::string extension = m_path.substr(dotIndex);
int width = 0;
int height = 0;
int channels = 0;
unsigned char* data = nullptr;
for (size_t i = 0; i < 6; ++i) {
std::string facePath = path + cubemapPaths[i] + extension;
// Load image data
data = stbi_load(facePath.c_str(), &width, &height, &channels, STBI_default);
VERIFY(data, "failed to load image: '{}'", facePath.c_str());
init(width, height, channels);
// Generate texture face
glTexImage2D(
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, // Texture target
0, // Midmap level, base starts at level 0
m_internalFormat, // Internal format
m_width, m_height, // Image width/height
0, // Always 0 (legacy)
m_dataFormat, // Texture source format
m_dataType, // Texture source datatype
data); // Image data
// Clean resources
stbi_image_free(data);
}
// Set the texture wrapping / filtering options
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Magnify
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Minify
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // X
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Y
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // Z
// Unbind texture object
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
// -----------------------------------------
std::shared_ptr<TextureFramebuffer> TextureFramebuffer::create(
std::string_view path,
uint32_t width, uint32_t height, uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType)
{
auto result = std::shared_ptr<TextureFramebuffer>(new TextureFramebuffer(path));
result->init(width, height, internalFormat, dataFormat, dataType);
result->createImpl();
return result;
}
void TextureFramebuffer::bind(uint32_t unit) const
{
// Set active unit
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, m_id);
// Reset unit
glActiveTexture(GL_TEXTURE0);
}
void TextureFramebuffer::unbind() const
{
glBindTexture(GL_TEXTURE_2D, 0);
}
void TextureFramebuffer::createImpl()
{
m_id = UINT_MAX;
// Create texture object
glGenTextures(1, &m_id);
// Bind texture object
glBindTexture(GL_TEXTURE_2D, m_id);
// Generate texture
glTexImage2D(
GL_TEXTURE_2D, // Texture target
0, // Midmap level, base starts at level 0
m_internalFormat, // Texture format
m_width, m_height, // Image width/height
0, // Always 0 (legacy)
m_dataFormat, // Texture source format
m_dataType, // Texture source datatype
NULL); // Image data
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Unbind texture object
glBindTexture(GL_TEXTURE_2D, 0);
}
} // namespace Inferno
+151
View File
@@ -0,0 +1,151 @@
/*
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstdint> // uint8_t, uint32_t
#include <memory> // std::shared_ptr
#include <string_view>
#include "glad/glad.h"
#include "inferno/asset/asset-manager.h"
struct aiTexture;
namespace Inferno {
class Texture : public Asset {
public:
virtual ~Texture();
static void savePNG(std::string_view path, std::shared_ptr<Texture> texture);
static void saveScreenshotPNG(std::string_view path, uint32_t width, uint32_t height);
void init(uint32_t width, uint32_t height, uint8_t channels);
void init(uint32_t width, uint32_t height, uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType);
virtual void bind(uint32_t unit = 0) const = 0;
virtual void unbind() const = 0;
uint32_t width() const { return m_width; }
uint32_t height() const { return m_height; }
uint32_t id() const { return m_id; }
uint32_t internalFormat() const { return m_internalFormat; }
uint32_t dataFormat() const { return m_dataFormat; }
uint32_t dataType() const { return m_dataType; }
virtual bool isTexture2D() const override { return false; }
virtual bool isTextureCubemap() const override { return false; }
virtual bool isTextureFramebuffer() const override { return false; }
protected:
Texture(std::string_view path)
: Asset(path)
{
}
protected:
uint32_t m_width { 0 };
uint32_t m_height { 0 };
uint32_t m_id { 0 };
uint32_t m_internalFormat { 0 };
uint32_t m_dataFormat { 0 };
uint32_t m_dataType { GL_UNSIGNED_BYTE };
private:
virtual bool isTexture() const override { return true; }
};
// -------------------------------------
class Texture2D final : public Texture {
public:
virtual ~Texture2D() = default;
// Factory function
static std::shared_ptr<Texture2D> create(std::string_view path);
static std::shared_ptr<Texture2D> create(aiTexture* texture);
virtual void bind(uint32_t unit = 0) const override;
virtual void unbind() const override;
private:
Texture2D(std::string_view path)
: Texture(path)
{
}
void createImpl(unsigned char* data);
virtual bool isTexture2D() const override { return true; }
};
// -------------------------------------
class TextureCubemap final : public Texture {
public:
virtual ~TextureCubemap() = default;
// Factory function
static std::shared_ptr<TextureCubemap> create(std::string_view path);
virtual void bind(uint32_t unit = 0) const override;
virtual void unbind() const override;
private:
TextureCubemap(std::string_view path)
: Texture(path)
{
}
void createImpl();
virtual bool isTextureCubemap() const override { return true; }
};
// -----------------------------------------
class TextureFramebuffer final : public Texture {
public:
virtual ~TextureFramebuffer() = default;
// Factory function
static std::shared_ptr<TextureFramebuffer> create(
std::string_view path,
uint32_t width, uint32_t height, uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType = GL_UNSIGNED_BYTE);
virtual void bind(uint32_t unit) const override;
virtual void unbind() const override;
private:
TextureFramebuffer(std::string_view path)
: Texture(path)
{
}
void createImpl();
virtual bool isTextureFramebuffer() const override { return true; }
};
// -----------------------------------------
// clang-format off
template<>
inline bool Asset::fastIs<Texture>() const { return isTexture(); }
template<>
inline bool Asset::fastIs<Texture2D>() const { return isTexture2D(); }
template<>
inline bool Asset::fastIs<TextureCubemap>() const { return isTextureCubemap(); }
template<>
inline bool Asset::fastIs<TextureFramebuffer>() const { return isTextureFramebuffer(); }
// clang-format on
} // namespace Inferno
+26
View File
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/format/print.h"
#include "ruc/json/json.h"
#include "inferno/component/cameracomponent.h"
namespace Inferno {
void fromJson(const ruc::Json& json, CameraComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("type")) {
value.type = json.at("type").get<std::string>() == "orthographic" ? CameraType::Orthographic : CameraType::Perspective;
}
if (json.exists("zoom-level")) {
json.at("zoom-level").getTo(value.zoomLevel);
}
}
} // namespace Inferno
+3
View File
@@ -8,6 +8,7 @@
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
#include "glm/ext/vector_float3.hpp" // glm::vec3
#include "ruc/json/json.h"
namespace Inferno {
@@ -32,4 +33,6 @@ struct CameraComponent {
glm::mat4 projection { 1.0f }; // Identity matrix
};
void fromJson(const ruc::Json& json, CameraComponent& value);
} // namespace Inferno
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/asset/texture.h"
#include "inferno/component/cubemap-component.h"
#include "inferno/component/serialize.h" // not detected as used by clang-tidy
namespace Inferno {
void fromJson(const ruc::Json& json, CubemapComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("color")) {
json.at("color").getTo(value.color);
}
if (json.exists("texture") && json.at("texture").type() == ruc::Json::Type::String) {
value.texture = AssetManager::the().load<TextureCubemap>(json.at("texture").asString());
}
if (json.exists("isLight")) {
json.at("isLight").getTo(value.isLight);
}
}
} // namespace Inferno
+26
View File
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <memory> // std::shared_ptr
#include "glm/ext/vector_float4.hpp" // glm::vec4
#include "ruc/json/json.h"
#include "inferno/asset/texture.h"
namespace Inferno {
struct CubemapComponent {
glm::vec4 color { 1.0f };
std::shared_ptr<Texture> texture;
bool isLight { false };
};
void fromJson(const ruc::Json& json, CubemapComponent& value);
} // namespace Inferno
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/component/id-component.h"
namespace Inferno {
void fromJson(const ruc::Json& json, IDComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("id")) {
json.at("id").getTo(value.id);
}
}
} // namespace Inferno
+28
View File
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "ruc/json/json.h"
#include "inferno/uid.h"
namespace Inferno {
struct IDComponent {
UID id;
IDComponent() = default;
IDComponent(UID id)
: id(id)
{
}
IDComponent(const IDComponent&) = default;
};
void fromJson(const ruc::Json& json, IDComponent& value);
} // namespace Inferno
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/component/luascriptcomponent.h"
namespace Inferno {
void fromJson(const ruc::Json& json, LuaScriptComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
VERIFY(json.exists("path"), "path not found");
json.at("path").getTo(value.path);
}
} // namespace Inferno
+6 -1
View File
@@ -6,9 +6,11 @@
#pragma once
#include <string> // std::string
#include <string>
#include <utility> // std::move
#include "ruc/json/json.h"
namespace Inferno {
class LuaScript;
@@ -24,4 +26,7 @@ struct LuaScriptComponent {
{
}
};
void fromJson(const ruc::Json& json, LuaScriptComponent& value);
} // namespace Inferno
+30
View File
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "inferno/component/model-component.h"
#include "inferno/asset/asset-manager.h"
#include "inferno/asset/model.h"
#include "inferno/asset/texture.h"
#include "inferno/component/serialize.h" // not detected as used by clang-tidy
namespace Inferno {
void fromJson(const ruc::Json& json, ModelComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("color")) {
json.at("color").getTo(value.color);
}
if (json.exists("model") && json.at("model").type() == ruc::Json::Type::String) {
value.model = AssetManager::the().load<Model>(json.at("model").asString());
}
if (json.exists("texture") && json.at("texture").type() == ruc::Json::Type::String) {
value.texture = AssetManager::the().load<Texture2D>(json.at("texture").asString());
}
}
} // namespace Inferno
+26
View File
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <memory> // std::shared_ptr
#include "ruc/json/json.h"
#include "inferno/asset/model.h"
#include "inferno/asset/texture.h"
namespace Inferno {
struct ModelComponent {
glm::vec4 color { 1.0f };
std::shared_ptr<Model> model;
std::shared_ptr<Texture2D> texture;
};
void fromJson(const ruc::Json& json, ModelComponent& value);
} // namespace Inferno
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/component/nativescriptcomponent.h"
namespace Inferno {
void fromJson(const ruc::Json& json, NativeScriptComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
VERIFY(json.exists("name"), "name not found");
json.at("name").getTo(value.name);
}
} // namespace Inferno
+16 -11
View File
@@ -6,7 +6,8 @@
#pragma once
#include "ruc/meta/assert.h"
#include <string>
#include <utility> // std::move
#include "inferno/script/nativescript.h"
@@ -14,25 +15,29 @@ namespace Inferno {
struct NativeScriptComponent {
NativeScript* instance { nullptr };
std::string name;
NativeScript* (*initialize)() { nullptr };
NativeScript::InitializeFunction initialize { nullptr };
NativeScript::DestroyFunction destroy { nullptr };
// Dont allow manually setting instance during construction
NativeScriptComponent() {}
template<typename T>
void bind()
NativeScriptComponent(const std::string& name)
: name(std::move(name))
{
VERIFY(instance == nullptr, "NativeScript already bound");
initialize = []() { return static_cast<NativeScript*>(new T()); };
bind();
}
void destroy()
void bind()
{
VERIFY(instance, "Attempting to destroy an uninitialized NativeScript");
delete instance;
instance = nullptr;
VERIFY(initialize == nullptr && destroy == nullptr, "NativeScript already bound");
VERIFY(name != "", "name not set");
initialize = NativeScriptBinding::the().initializeBinding(name);
destroy = NativeScriptBinding::the().destroyBinding(name);
}
};
void fromJson(const ruc::Json& json, NativeScriptComponent& value);
} // namespace Inferno
+127
View File
@@ -0,0 +1,127 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "glm/ext/matrix_float2x2.hpp"
#include "glm/ext/matrix_float3x3.hpp"
#include "glm/ext/matrix_float4x4.hpp"
#include "glm/ext/vector_float2.hpp"
#include "glm/ext/vector_float3.hpp"
#include "glm/ext/vector_float4.hpp"
#include "ruc/json/json.h"
#include "inferno/component/serialize.h"
namespace glm {
void toJson(ruc::Json& json, const vec2& value)
{
json = ruc::Json {
{ value.x, value.y },
};
}
void fromJson(const ruc::Json& json, vec2& value)
{
VERIFY(json.type() == ruc::Json::Type::Array);
auto& values = json.asArray();
VERIFY(values.size() == 2, "glm::vec2 expected 2 values, got: {}", values.size());
value.x = values.at(0).get<float>();
value.y = values.at(1).get<float>();
}
// -----------------------------------------
void toJson(ruc::Json& json, const vec3& value)
{
json = ruc::Json {
{ value.x, value.y, value.z },
};
}
void fromJson(const ruc::Json& json, vec3& value)
{
VERIFY(json.type() == ruc::Json::Type::Array);
auto& values = json.asArray();
VERIFY(values.size() == 3, "glm::vec3 expected 3 values, got: {}", values.size());
value.x = values.at(0).get<float>();
value.y = values.at(1).get<float>();
value.z = values.at(2).get<float>();
}
// -----------------------------------------
void toJson(ruc::Json& json, const vec4& value)
{
json = ruc::Json {
{ value.r, value.g, value.b, value.a },
};
}
void fromJson(const ruc::Json& json, vec4& value)
{
VERIFY(json.type() == ruc::Json::Type::Array);
auto& values = json.asArray();
VERIFY(values.size() == 4, "glm::vec4 expected 4 values, got: {}", values.size());
value.r = values.at(0).get<float>();
value.g = values.at(1).get<float>();
value.b = values.at(2).get<float>();
value.a = values.at(3).get<float>();
}
} // namespace glm
// -----------------------------------------
void ruc::format::Formatter<glm::vec2>::format(Builder& builder, glm::vec2 value) const
{
return Formatter<std::vector<float>>::format(builder, { value.x, value.y });
}
void ruc::format::Formatter<glm::vec3>::format(Builder& builder, glm::vec3 value) const
{
return Formatter<std::vector<float>>::format(builder, { value.x, value.y, value.z });
}
void ruc::format::Formatter<glm::vec4>::format(Builder& builder, glm::vec4 value) const
{
return Formatter<std::vector<float>>::format(builder, { value.x, value.y, value.z, value.w });
}
void ruc::format::Formatter<glm::mat2>::format(Builder& builder, glm::mat2 value) const
{
builder.putString("mat2 ");
Formatter<glm::vec2>::format(builder, value[0]);
builder.putString("\n ");
return Formatter<glm::vec2>::format(builder, value[1]);
}
void ruc::format::Formatter<glm::mat3>::format(Builder& builder, glm::mat3 value) const
{
builder.putString("mat3 ");
Formatter<glm::vec3>::format(builder, value[0]);
builder.putString("\n ");
Formatter<glm::vec3>::format(builder, value[1]);
builder.putString("\n ");
return Formatter<glm::vec3>::format(builder, value[2]);
}
void ruc::format::Formatter<glm::mat4>::format(Builder& builder, glm::mat4 value) const
{
builder.putString("mat4 ");
Formatter<glm::vec4>::format(builder, value[0]);
builder.putString("\n ");
Formatter<glm::vec4>::format(builder, value[1]);
builder.putString("\n ");
Formatter<glm::vec4>::format(builder, value[2]);
builder.putString("\n ");
return Formatter<glm::vec4>::format(builder, value[3]);
}
+58
View File
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "glm/ext/matrix_float2x2.hpp"
#include "glm/ext/matrix_float3x3.hpp"
#include "glm/ext/matrix_float4x4.hpp"
#include "glm/ext/vector_float2.hpp"
#include "glm/ext/vector_float3.hpp"
#include "glm/ext/vector_float4.hpp"
#include "ruc/json/json.h"
namespace glm {
void toJson(ruc::Json& json, const vec2& value);
void fromJson(const ruc::Json& json, vec2& value);
void toJson(ruc::Json& json, const vec3& value);
void fromJson(const ruc::Json& json, vec3& value);
void toJson(ruc::Json& json, const vec4& value);
void fromJson(const ruc::Json& json, vec4& value);
} // namespace glm
template<>
struct ruc::format::Formatter<glm::vec2> : Formatter<std::vector<float>> {
void format(Builder& builder, glm::vec2 value) const;
};
template<>
struct ruc::format::Formatter<glm::vec3> : Formatter<std::vector<float>> {
void format(Builder& builder, glm::vec3 value) const;
};
template<>
struct ruc::format::Formatter<glm::vec4> : Formatter<std::vector<float>> {
void format(Builder& builder, glm::vec4 value) const;
};
template<>
struct ruc::format::Formatter<glm::mat2> : Formatter<glm::vec2> {
void format(Builder& builder, glm::mat2 value) const;
};
template<>
struct ruc::format::Formatter<glm::mat3> : Formatter<glm::vec3> {
void format(Builder& builder, glm::mat3 value) const;
};
template<>
struct ruc::format::Formatter<glm::mat4> : Formatter<glm::vec4> {
void format(Builder& builder, glm::mat4 value) const;
};
+6 -26
View File
@@ -4,8 +4,12 @@
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/asset/asset-manager.h"
#include "inferno/asset/texture.h"
#include "inferno/component/serialize.h" // not detected as used by clang-tidy
#include "inferno/component/spritecomponent.h"
#include "inferno/render/texture.h"
namespace Inferno {
@@ -17,32 +21,8 @@ void fromJson(const ruc::Json& json, SpriteComponent& value)
json.at("color").getTo(value.color);
}
if (json.exists("texture") && json.at("texture").type() == ruc::Json::Type::String) {
value.texture = TextureManager::the().load(json.at("texture").asString());
value.texture = AssetManager::the().load<Texture2D>(json.at("texture").asString());
}
}
} // namespace Inferno
namespace glm {
void toJson(ruc::Json& json, const vec4& value)
{
json = ruc::Json {
{ value.r, value.g, value.b, value.a },
};
}
void fromJson(const ruc::Json& json, vec4& value)
{
VERIFY(json.type() == ruc::Json::Type::Array);
auto& values = json.asArray();
VERIFY(values.size() == 4, "glm::vec4 expects 4 values, not {}", values.size());
value.r = values.at(0).get<float>();
value.g = values.at(1).get<float>();
value.b = values.at(2).get<float>();
value.a = values.at(3).get<float>();
}
} // namespace glm
+1 -8
View File
@@ -11,7 +11,7 @@
#include "glm/ext/vector_float4.hpp" // glm::vec4
#include "ruc/json/json.h"
#include "inferno/render/texture.h"
#include "inferno/asset/texture.h"
namespace Inferno {
@@ -23,10 +23,3 @@ struct SpriteComponent {
void fromJson(const ruc::Json& json, SpriteComponent& value);
} // namespace Inferno
namespace glm {
void toJson(ruc::Json& json, const vec4& value);
void fromJson(const ruc::Json& json, vec4& value);
} // namespace glm
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/component/tagcomponent.h"
namespace Inferno {
void fromJson(const ruc::Json& json, TagComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("tag")) {
json.at("tag").getTo(value.tag);
}
}
} // namespace Inferno
+4
View File
@@ -9,6 +9,8 @@
#include <string> // std::string
#include <utility> // std::move
#include "ruc/json/json.h"
namespace Inferno {
struct TagComponent {
@@ -23,4 +25,6 @@ struct TagComponent {
operator const std::string&() const { return tag; }
};
void fromJson(const ruc::Json& json, TagComponent& value);
} // namespace Inferno
+3 -3
View File
@@ -23,12 +23,12 @@ void fromJson(const ruc::Json& json, TextAreaComponent& value)
if (json.exists("font-size") && json.at("font-size").type() == ruc::Json::Type::Number) {
json.at("font-size").getTo(value.fontSize);
}
if (json.exists("line-spacing") && json.at("line-spacing").type() == ruc::Json::Type::Number) {
json.at("line-spacing").getTo(value.lineSpacing);
}
if (json.exists("width") && json.at("width").type() == ruc::Json::Type::Number) {
json.at("width").getTo(value.width);
}
if (json.exists("lines") && json.at("lines").type() == ruc::Json::Type::Number) {
json.at("lines").getTo(value.width);
}
}
} // namespace Inferno
+3 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022-2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
@@ -18,9 +18,9 @@ namespace Inferno {
struct TextAreaComponent {
std::string content;
std::string font;
uint32_t fontSize { 0 };
unsigned char fontSize { 12 };
float lineSpacing { 1.0f };
uint32_t width { 0 };
uint32_t lines { 0 };
#if 0
TextAreaComponent() {}
+1 -50
View File
@@ -7,6 +7,7 @@
#include "ruc/format/format.h"
#include "ruc/json/json.h"
#include "inferno/component/serialize.h"
#include "inferno/component/transformcomponent.h"
namespace Inferno {
@@ -28,56 +29,6 @@ void fromJson(const ruc::Json& json, TransformComponent& value)
} // namespace Inferno
namespace glm {
void toJson(ruc::Json& json, const vec3& value)
{
json = ruc::Json {
{ value.x, value.y, value.z },
};
}
void fromJson(const ruc::Json& json, vec3& value)
{
VERIFY(json.type() == ruc::Json::Type::Array);
auto& values = json.asArray();
VERIFY(values.size() == 3, "glm::vec3 expects 3 values, not {}", values.size());
value.x = values.at(0).get<float>();
value.y = values.at(1).get<float>();
value.z = values.at(2).get<float>();
}
} // namespace glm
void ruc::format::Formatter<glm::vec2>::format(Builder& builder, glm::vec2 value) const
{
return Formatter<std::vector<float>>::format(builder, { value.x, value.y });
}
void ruc::format::Formatter<glm::vec3>::format(Builder& builder, glm::vec3 value) const
{
return Formatter<std::vector<float>>::format(builder, { value.x, value.y, value.z });
}
void ruc::format::Formatter<glm::vec4>::format(Builder& builder, glm::vec4 value) const
{
return Formatter<std::vector<float>>::format(builder, { value.x, value.y, value.z, value.w });
}
void ruc::format::Formatter<glm::mat4>::format(Builder& builder, glm::mat4 value) const
{
builder.putString("mat4 ");
Formatter<glm::vec4>::format(builder, value[0]);
builder.putString("\n ");
Formatter<glm::vec4>::format(builder, value[1]);
builder.putString("\n ");
Formatter<glm::vec4>::format(builder, value[2]);
builder.putString("\n ");
return Formatter<glm::vec4>::format(builder, value[3]);
}
void ruc::format::Formatter<Inferno::TransformComponent>::format(Builder& builder, Inferno::TransformComponent value) const
{
builder.putString("transform ");
+5 -28
View File
@@ -1,11 +1,13 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "entt/entity/entity.hpp" // entt::null
#include "entt/entity/fwd.hpp" // entt::entity
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
#include "glm/ext/vector_float3.hpp" // glm::vec3
#include "ruc/format/format.h"
@@ -17,6 +19,8 @@ struct TransformComponent {
glm::vec3 translate { 0.0f, 0.0f, 0.0f };
glm::vec3 rotate { 0.0f, 0.0f, 0.0f };
glm::vec3 scale { 1.0f, 1.0f, 1.0f };
entt::entity parent { entt::null };
glm::mat4 transform { 1.0f }; // Identity matrix
};
@@ -24,33 +28,6 @@ void fromJson(const ruc::Json& json, TransformComponent& value);
} // namespace Inferno
namespace glm {
void toJson(ruc::Json& json, const vec3& value);
void fromJson(const ruc::Json& json, vec3& value);
} // namespace glm
template<>
struct ruc::format::Formatter<glm::vec2> : Formatter<std::vector<float>> {
void format(Builder& builder, glm::vec2 value) const;
};
template<>
struct ruc::format::Formatter<glm::vec3> : Formatter<std::vector<float>> {
void format(Builder& builder, glm::vec3 value) const;
};
template<>
struct ruc::format::Formatter<glm::vec4> : Formatter<std::vector<float>> {
void format(Builder& builder, glm::vec4 value) const;
};
template<>
struct ruc::format::Formatter<glm::mat4> : Formatter<glm::vec4> {
void format(Builder& builder, glm::mat4 value) const;
};
template<>
struct ruc::format::Formatter<Inferno::TransformComponent> : Formatter<glm::vec3> {
void format(Builder& builder, Inferno::TransformComponent value) const;
+17 -14
View File
@@ -14,24 +14,27 @@ namespace Inferno {
class KeyEvent : public Event {
public:
inline int getKey() const { return m_key; }
int getKey() const { return m_key; }
int getMods() const { return m_mods; }
EVENT_CLASS_CATEGORY(InputEventCategory | KeyEventCategory)
protected:
KeyEvent(int key)
KeyEvent(int key, int mods)
: m_key(key)
, m_mods(mods)
{
}
private:
int m_key;
int m_key { 0 };
int m_mods { 0 };
};
class KeyPressEvent : public KeyEvent {
class KeyPressEvent final : public KeyEvent {
public:
KeyPressEvent(int key)
: KeyEvent(key)
KeyPressEvent(int key, int mods)
: KeyEvent(key, mods)
{
}
@@ -45,10 +48,10 @@ public:
EVENT_CLASS_TYPE(KeyPress)
};
class KeyReleaseEvent : public KeyEvent {
class KeyReleaseEvent final : public KeyEvent {
public:
KeyReleaseEvent(int key)
: KeyEvent(key)
KeyReleaseEvent(int key, int mods)
: KeyEvent(key, mods)
{
}
@@ -59,13 +62,13 @@ public:
return ss.str();
}
EVENT_CLASS_TYPE(KeyPress)
EVENT_CLASS_TYPE(KeyRelease)
};
class KeyRepeatEvent : public KeyEvent {
class KeyRepeatEvent final : public KeyEvent {
public:
KeyRepeatEvent(int key)
: KeyEvent(key)
KeyRepeatEvent(int key, int mods)
: KeyEvent(key, mods)
{
}
@@ -76,7 +79,7 @@ public:
return ss.str();
}
EVENT_CLASS_TYPE(KeyPress)
EVENT_CLASS_TYPE(KeyRepeat)
};
} // namespace Inferno
+19
View File
@@ -139,14 +139,33 @@ static std::unordered_map<std::string_view, int> keys({
{ MAP_KEY(GLFW_KEY_MENU) },
});
static std::unordered_map<std::string_view, int> modifiers({
{ MAP_KEY(GLFW_MOD_SHIFT) },
{ MAP_KEY(GLFW_MOD_CONTROL) },
{ MAP_KEY(GLFW_MOD_ALT) },
{ MAP_KEY(GLFW_MOD_SUPER) },
{ MAP_KEY(GLFW_MOD_CAPS_LOCK) }, // State, not really a modifier
{ MAP_KEY(GLFW_MOD_NUM_LOCK) }, // State, not really a modifier
});
// -----------------------------------------
// Example usage:
// event.getKey() == keyCode("GLFW_KEY_ESCAPE")
int keyCode(std::string_view name)
{
VERIFY(keys.find(name) != keys.end(), "could not find key code: {}", name);
return keys.at(name);
}
// Example usage:
// event.getMods() & keyMod("GLFW_MOD_SHIFT")
int keyMod(std::string_view name)
{
VERIFY(modifiers.find(name) != modifiers.end(), "could not find key modifier: {}", name);
return modifiers.at(name);
}
std::string_view keyName(int key)
{
auto it = std::find_if(keys.begin(), keys.end(), [key](const auto& keybind) {
+3 -1
View File
@@ -13,6 +13,8 @@
namespace Inferno {
int keyCode(std::string_view name);
std::string_view keyName(int);
int keyMod(std::string_view name);
std::string_view keyName(int key);
} // namespace Inferno
+177 -116
View File
@@ -4,12 +4,17 @@
* SPDX-License-Identifier: MIT
*/
#include "glad/glad.h"
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint8_t, uint32_t
#include <memory> // std::shared_ptr
#include <string>
#include <utility> // std::pair
#include "inferno/core.h"
#include "inferno/render/buffer.h"
#include "glad/glad.h"
#include "ruc/meta/assert.h"
#include "inferno/render/buffer.h"
namespace Inferno {
// -----------------------------------------
@@ -37,113 +42,78 @@ uint32_t BufferElement::getTypeGL() const
return BufferElement::getTypeGL(m_type);
}
uint32_t BufferElement::getTypeSize(const BufferElementType type)
uint32_t BufferElement::getTypeSize(BufferElementType type)
{
switch (type) {
case BufferElementType::None:
return 0;
case BufferElementType::Bool:
return sizeof(bool);
case BufferElementType::Bool2:
return sizeof(bool) * 2;
case BufferElementType::Bool3:
return sizeof(bool) * 3;
case BufferElementType::Bool4:
return sizeof(bool) * 4;
return sizeof(bool) * getTypeCount(type);
case BufferElementType::Int:
return sizeof(int32_t);
case BufferElementType::Int2:
return sizeof(int32_t) * 2;
case BufferElementType::Int3:
return sizeof(int32_t) * 3;
case BufferElementType::Int4:
return sizeof(int32_t) * 4;
return sizeof(int32_t) * getTypeCount(type);
case BufferElementType::Uint:
return sizeof(uint32_t);
case BufferElementType::Uint2:
return sizeof(uint32_t) * 2;
case BufferElementType::Uint3:
return sizeof(uint32_t) * 3;
case BufferElementType::Uint4:
return sizeof(uint32_t) * 4;
return sizeof(uint32_t) * getTypeCount(type);
case BufferElementType::Float:
return sizeof(float);
case BufferElementType::Vec2:
return sizeof(float) * 2;
case BufferElementType::Vec3:
return sizeof(float) * 3;
case BufferElementType::Vec4:
return sizeof(float) * 4;
return sizeof(float) * getTypeCount(type);
case BufferElementType::Double:
return sizeof(double);
case BufferElementType::Double2:
return sizeof(double);
case BufferElementType::Double3:
return sizeof(double);
case BufferElementType::Double4:
return sizeof(double);
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
return sizeof(double) * getTypeCount(type);
case BufferElementType::Mat2:
return sizeof(float) * 2 * 2;
case BufferElementType::Mat3:
return sizeof(float) * 3 * 3;
case BufferElementType::Mat4:
return sizeof(float) * 4 * 4;
case BufferElementType::DoubleMat2:
return sizeof(double) * 2 * 2;
case BufferElementType::DoubleMat3:
return sizeof(double) * 3 * 3;
case BufferElementType::DoubleMat4:
return sizeof(double) * 4 * 4;
return sizeof(float) * getTypeCount(type);
case BufferElementType::MatDouble2:
case BufferElementType::MatDouble3:
case BufferElementType::MatDouble4:
return sizeof(double) * getTypeCount(type);
};
VERIFY(false, "BufferElement unknown BufferElementType size!");
return 0;
}
uint32_t BufferElement::getTypeCount(const BufferElementType type)
uint32_t BufferElement::getTypeCount(BufferElementType type)
{
switch (type) {
case BufferElementType::None:
return 0;
case BufferElementType::Bool:
return 1;
case BufferElementType::Bool2:
return 2;
case BufferElementType::Bool3:
return 3;
case BufferElementType::Bool4:
return 4;
case BufferElementType::Int:
return 1;
case BufferElementType::Int2:
return 2;
case BufferElementType::Int3:
return 3;
case BufferElementType::Int4:
return 4;
case BufferElementType::Uint:
return 1;
case BufferElementType::Uint2:
return 2;
case BufferElementType::Uint3:
return 3;
case BufferElementType::Uint4:
return 4;
case BufferElementType::Float:
return 1;
case BufferElementType::Vec2:
return 2;
case BufferElementType::Vec3:
return 3;
case BufferElementType::Vec4:
return 4;
case BufferElementType::Double:
return 1;
case BufferElementType::Double2:
case BufferElementType::Bool2:
case BufferElementType::Int2:
case BufferElementType::Uint2:
case BufferElementType::Vec2:
case BufferElementType::Vec2Double:
return 2;
case BufferElementType::Double3:
case BufferElementType::Bool3:
case BufferElementType::Int3:
case BufferElementType::Uint3:
case BufferElementType::Vec3:
case BufferElementType::Vec3Double:
return 3;
case BufferElementType::Double4:
case BufferElementType::Bool4:
case BufferElementType::Int4:
case BufferElementType::Uint4:
case BufferElementType::Vec4:
case BufferElementType::Vec4Double:
return 4;
case BufferElementType::Mat2:
return 2 * 2;
@@ -151,11 +121,11 @@ uint32_t BufferElement::getTypeCount(const BufferElementType type)
return 3 * 3;
case BufferElementType::Mat4:
return 4 * 4;
case BufferElementType::DoubleMat2:
case BufferElementType::MatDouble2:
return 2 * 2;
case BufferElementType::DoubleMat3:
case BufferElementType::MatDouble3:
return 3 * 3;
case BufferElementType::DoubleMat4:
case BufferElementType::MatDouble4:
return 4 * 4;
};
@@ -163,62 +133,43 @@ uint32_t BufferElement::getTypeCount(const BufferElementType type)
return 0;
}
uint32_t BufferElement::getTypeGL(const BufferElementType type)
uint32_t BufferElement::getTypeGL(BufferElementType type)
{
switch (type) {
case BufferElementType::None:
return GL_NONE;
case BufferElementType::Bool:
return GL_BOOL;
case BufferElementType::Bool2:
return GL_BOOL;
case BufferElementType::Bool3:
return GL_BOOL;
case BufferElementType::Bool4:
return GL_BOOL;
case BufferElementType::Int:
return GL_INT;
case BufferElementType::Int2:
return GL_INT;
case BufferElementType::Int3:
return GL_INT;
case BufferElementType::Int4:
return GL_INT;
case BufferElementType::Uint:
return GL_UNSIGNED_INT;
case BufferElementType::Uint2:
return GL_UNSIGNED_INT;
case BufferElementType::Uint3:
return GL_UNSIGNED_INT;
case BufferElementType::Uint4:
return GL_UNSIGNED_INT;
case BufferElementType::Float:
return GL_FLOAT;
case BufferElementType::Vec2:
return GL_FLOAT;
case BufferElementType::Vec3:
return GL_FLOAT;
case BufferElementType::Vec4:
return GL_FLOAT;
case BufferElementType::Double:
return GL_DOUBLE;
case BufferElementType::Double2:
return GL_DOUBLE;
case BufferElementType::Double3:
return GL_DOUBLE;
case BufferElementType::Double4:
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
return GL_DOUBLE;
case BufferElementType::Mat2:
return GL_FLOAT;
case BufferElementType::Mat3:
return GL_FLOAT;
case BufferElementType::Mat4:
return GL_FLOAT;
case BufferElementType::DoubleMat2:
return GL_DOUBLE;
case BufferElementType::DoubleMat3:
return GL_DOUBLE;
case BufferElementType::DoubleMat4:
case BufferElementType::MatDouble2:
case BufferElementType::MatDouble3:
case BufferElementType::MatDouble4:
return GL_DOUBLE;
};
@@ -226,6 +177,58 @@ uint32_t BufferElement::getTypeGL(const BufferElementType type)
return 0;
}
uint32_t BufferElement::getGLTypeSize(uint32_t type)
{
switch (type) {
case GL_BOOL:
case GL_INT:
case GL_UNSIGNED_INT:
case GL_FLOAT:
return 4;
case GL_BOOL_VEC2:
case GL_INT_VEC2:
case GL_UNSIGNED_INT_VEC2:
case GL_FLOAT_VEC2:
return 4 * 2;
case GL_BOOL_VEC3:
case GL_INT_VEC3:
case GL_UNSIGNED_INT_VEC3:
case GL_FLOAT_VEC3:
return 4 * 3;
case GL_BOOL_VEC4:
case GL_INT_VEC4:
case GL_UNSIGNED_INT_VEC4:
case GL_FLOAT_VEC4:
return 4 * 4;
case GL_FLOAT_MAT2:
return 4 * 2 * 2;
case GL_FLOAT_MAT3:
return 4 * 3 * 3;
case GL_FLOAT_MAT4:
return 4 * 4 * 4;
case GL_DOUBLE:
return 8;
case GL_DOUBLE_VEC2:
return 8 * 2;
case GL_DOUBLE_VEC3:
return 8 * 3;
case GL_DOUBLE_VEC4:
return 8 * 4;
case GL_DOUBLE_MAT2:
return 8 * 2 * 2;
case GL_DOUBLE_MAT3:
return 8 * 3 * 3;
case GL_DOUBLE_MAT4:
return 8 * 4 * 4;
default:
VERIFY_NOT_REACHED();
};
return 0;
}
// -----------------------------------------
BufferLayout::BufferLayout(const std::initializer_list<BufferElement>& elements)
@@ -239,30 +242,25 @@ void BufferLayout::calculateOffsetsAndStride()
m_stride = 0;
for (auto& element : m_elements) {
element.setOffset(m_stride);
m_stride += element.getSize();
m_stride += element.size();
}
}
// -----------------------------------------
VertexBuffer::VertexBuffer(size_t size)
: VertexBuffer(size, nullptr)
{
glGenBuffers(1, &m_id);
bind();
// Reserve data on the GPU
glBufferData(GL_ARRAY_BUFFER, size, nullptr, GL_DYNAMIC_DRAW);
unbind();
}
VertexBuffer::VertexBuffer(float* vertices, size_t size)
VertexBuffer::VertexBuffer(size_t size, float* vertices)
{
m_id = UINT_MAX;
glGenBuffers(1, &m_id);
bind();
// Upload data to the GPU
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_DYNAMIC_DRAW);
unbind();
}
@@ -297,11 +295,12 @@ void VertexBuffer::uploadData(const void* data, uint32_t size)
IndexBuffer::IndexBuffer(uint32_t* indices, size_t size)
: m_count(size / sizeof(uint32_t))
{
m_id = UINT_MAX;
glCreateBuffers(1, &m_id);
bind();
// Upload data to the GPU
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_DYNAMIC_DRAW);
unbind();
}
@@ -321,10 +320,21 @@ void IndexBuffer::unbind() const
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void IndexBuffer::uploadData(const void* data, uint32_t size)
{
bind();
// Upload data to the GPU
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, size, data);
unbind();
}
// -----------------------------------------
VertexArray::VertexArray()
{
m_id = UINT_MAX;
glCreateVertexArrays(1, &m_id);
}
@@ -345,8 +355,8 @@ void VertexArray::unbind() const
void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
{
const auto& layout = vertexBuffer->getLayout();
VERIFY(layout.getElements().size(), "VertexBuffer has no layout");
const auto& layout = vertexBuffer->layout();
VERIFY(layout.elements().size(), "VertexBuffer has no layout");
bind();
vertexBuffer->bind();
@@ -354,20 +364,71 @@ void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
uint32_t index = 0;
for (const auto& element : layout) {
glEnableVertexAttribArray(index);
switch (element.type()) {
case BufferElementType::None:
break;
case BufferElementType::Int:
case BufferElementType::Int2:
case BufferElementType::Int3:
case BufferElementType::Int4:
case BufferElementType::Uint:
case BufferElementType::Uint2:
case BufferElementType::Uint3:
case BufferElementType::Uint4: {
glVertexAttribIPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
layout.stride(),
reinterpret_cast<const void*>(element.offset()));
break;
}
case BufferElementType::Bool:
case BufferElementType::Bool2:
case BufferElementType::Bool3:
case BufferElementType::Bool4:
case BufferElementType::Float:
case BufferElementType::Vec2:
case BufferElementType::Vec3:
case BufferElementType::Vec4:
case BufferElementType::Mat2:
case BufferElementType::Mat3:
case BufferElementType::Mat4: {
glVertexAttribPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
element.getNormalized() ? GL_TRUE : GL_FALSE,
layout.getStride(),
reinterpret_cast<const void*>(element.getOffset()));
element.normalized() ? GL_TRUE : GL_FALSE,
layout.stride(),
reinterpret_cast<const void*>(element.offset()));
break;
}
case BufferElementType::Double:
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
case BufferElementType::MatDouble2:
case BufferElementType::MatDouble3:
case BufferElementType::MatDouble4: {
glVertexAttribLPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
layout.stride(),
reinterpret_cast<const void*>(element.offset()));
break;
}
default:
VERIFY_NOT_REACHED();
};
index++;
}
m_vertexBuffers.push_back(std::move(vertexBuffer));
unbind();
vertexBuffer->unbind();
m_vertexBuffers.push_back(std::move(vertexBuffer));
}
void VertexArray::setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer)
@@ -375,10 +436,10 @@ void VertexArray::setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer)
bind();
indexBuffer->bind();
m_indexBuffer = std::move(indexBuffer);
unbind();
indexBuffer->unbind();
m_indexBuffer = std::move(indexBuffer);
}
} // namespace Inferno
+42 -37
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
@@ -7,52 +7,54 @@
#pragma once
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint32_t
#include <cstdint> // int32_t, uint8_t, uint32_t
#include <memory> // std::shared_ptr
#include <string> // std::string
#include <vector> // std::vector
#include <string>
#include <vector>
namespace Inferno {
// clang-format off
// https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)
enum class BufferElementType {
enum class BufferElementType : uint8_t {
None = 0,
Bool, Bool2, Bool3, Bool4, // bvec
Int, Int2, Int3, Int4, // ivec
Uint, Uint2, Uint3, Uint4, // uvec
Float, Vec2, Vec3, Vec4, // vec
Double, Double2, Double3, Double4, // dvec
Double, Vec2Double, Vec3Double, Vec4Double, // dvec
Mat2, Mat3, Mat4, // mat
DoubleMat2, DoubleMat3, DoubleMat4, // dmat
MatDouble2, MatDouble3, MatDouble4, // dmat
};
// clang-format on
// -----------------------------------------
// Describes one element of the BufferLayout
class BufferElement {
class BufferElement final {
public:
BufferElement(BufferElementType type, std::string name, bool normalized = false);
~BufferElement() = default;
uint32_t getTypeSize() const;
uint32_t getTypeCount() const;
uint32_t getTypeGL() const;
static uint32_t getTypeSize(const BufferElementType type);
static uint32_t getTypeCount(const BufferElementType type);
static uint32_t getTypeGL(const BufferElementType type);
static uint32_t getTypeSize(BufferElementType type);
static uint32_t getTypeCount(BufferElementType type);
static uint32_t getTypeGL(BufferElementType type);
static uint32_t getGLTypeSize(uint32_t type);
inline BufferElementType getType() const { return m_type; }
inline std::string getName() const { return m_name; }
inline uint32_t getSize() const { return m_size; }
inline uint32_t getOffset() const { return m_offset; }
inline bool getNormalized() const { return m_normalized; }
BufferElementType type() const { return m_type; }
std::string name() const { return m_name; }
uint32_t size() const { return m_size; }
uint32_t offset() const { return m_offset; }
bool normalized() const { return m_normalized; }
inline void setType(const BufferElementType& type) { m_type = type; }
inline void setName(const std::string& name) { m_name = name; }
inline void setSize(const uint32_t& size) { m_size = size; }
inline void setOffset(const uint32_t& offset) { m_offset = offset; }
inline void setNormalized(const bool& normalized) { m_normalized = normalized; }
void setType(BufferElementType type) { m_type = type; }
void setName(const std::string& name) { m_name = name; }
void setSize(uint32_t size) { m_size = size; }
void setOffset(uint32_t offset) { m_offset = offset; }
void setNormalized(bool normalized) { m_normalized = normalized; }
private:
BufferElementType m_type;
@@ -65,19 +67,20 @@ private:
// -----------------------------------------
// Layout that describes raw vertex data
class BufferLayout {
class BufferLayout final {
public:
BufferLayout() {}
BufferLayout(const std::initializer_list<BufferElement>& elements);
~BufferLayout() = default;
inline const std::vector<BufferElement>& getElements() const { return m_elements; }
inline uint32_t getStride() const { return m_stride; }
const std::vector<BufferElement>& elements() const { return m_elements; }
uint32_t stride() const { return m_stride; }
// Iterators
inline std::vector<BufferElement>::iterator begin() { return m_elements.begin(); }
inline std::vector<BufferElement>::iterator end() { return m_elements.end(); }
inline std::vector<BufferElement>::const_iterator begin() const { return m_elements.begin(); }
inline std::vector<BufferElement>::const_iterator end() const { return m_elements.end(); }
std::vector<BufferElement>::iterator begin() { return m_elements.begin(); }
std::vector<BufferElement>::iterator end() { return m_elements.end(); }
std::vector<BufferElement>::const_iterator begin() const { return m_elements.begin(); }
std::vector<BufferElement>::const_iterator end() const { return m_elements.end(); }
protected:
void calculateOffsetsAndStride();
@@ -90,10 +93,10 @@ private:
// -----------------------------------------
// GPU memory which holds raw vertex data
class VertexBuffer {
class VertexBuffer final { // Vertex Buffer Object, VBO
public:
VertexBuffer(size_t size);
VertexBuffer(float* vertices, size_t size);
VertexBuffer(size_t size, float* vertices);
~VertexBuffer();
void bind() const;
@@ -101,9 +104,9 @@ public:
void uploadData(const void* data, uint32_t size);
inline const BufferLayout& getLayout() const { return m_layout; }
const BufferLayout& layout() const { return m_layout; }
inline void setLayout(const BufferLayout& layout) { m_layout = layout; }
void setLayout(const BufferLayout& layout) { m_layout = layout; }
private:
uint32_t m_id { 0 };
@@ -113,7 +116,7 @@ private:
// -----------------------------------------
// Vertices order of rendering
class IndexBuffer {
class IndexBuffer final { // Element Buffer Object, EBO
public:
IndexBuffer(uint32_t* indices, size_t size);
~IndexBuffer();
@@ -121,7 +124,9 @@ public:
void bind() const;
void unbind() const;
inline uint32_t getCount() const { return m_count; }
void uploadData(const void* data, uint32_t size);
uint32_t count() const { return m_count; }
private:
uint32_t m_id { 0 };
@@ -131,7 +136,7 @@ private:
// -----------------------------------------
// Array that holds the vertex attributes configuration
class VertexArray {
class VertexArray final { // Vertex Array Object, VAO
public:
VertexArray();
~VertexArray();
@@ -142,8 +147,8 @@ public:
void addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer);
void setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer);
inline const std::vector<std::shared_ptr<VertexBuffer>>& getVertexBuffers() const { return m_vertexBuffers; }
inline std::shared_ptr<IndexBuffer> getIndexBuffer() const { return m_indexBuffer; }
std::shared_ptr<VertexBuffer> at(size_t i) const { return m_vertexBuffers.at(i); }
std::shared_ptr<IndexBuffer> indexBuffer() const { return m_indexBuffer; }
private:
uint32_t m_id { 0 };
-168
View File
@@ -1,168 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <limits> // std::numeric_limits
#include <string> // std::getline, std::stoi
#include <utility> // std::move
#include "ruc/file.h"
#include "ruc/format/log.h"
#include "ruc/meta/assert.h"
#include "inferno/render/font.h"
#include "inferno/render/texture.h"
#include "inferno/util/integer.h"
namespace Inferno {
Font::Font(const std::string& name)
: m_name(std::move(name))
{
std::string path = name + ".fnt";
std::string image = name + ".png";
std::string font = ruc::File(path).data();
parseFont(font);
m_texture = std::make_shared<Texture>(image);
}
void Font::parseFont(const std::string& font)
{
std::istringstream iss(font);
for (std::string line; std::getline(iss, line);) {
if (findAction(line).compare("info") != 0 && findAction(line).compare("char") != 0) {
continue;
}
const std::vector<std::string> columns = findColumns(line);
// Info
if (findAction(line).compare("info") == 0) {
m_size = std::stou(findValue("size", columns));
continue;
}
// Character
unsigned char id = std::stoi(findValue("id", columns));
Character character = {
{ std::stou(findValue("x", columns)), std::stou(findValue("y", columns)) },
{ std::stou(findValue("width", columns)), std::stou(findValue("height", columns)) },
{ std::stoi(findValue("xoffset", columns)), std::stoi(findValue("yoffset", columns)) },
std::stou(findValue("xadvance", columns))
};
m_characterList.emplace(id, std::make_shared<Character>(character));
}
}
const std::string Font::findAction(const std::string& line)
{
return line.substr(0, line.find(" "));
}
const std::vector<std::string> Font::findColumns(const std::string& line)
{
std::vector<std::string> elements;
size_t index = 0;
size_t find = 0;
size_t findFirstNotOf = 0;
// Loop over line characters
while (find != std::string::npos) {
find = line.find(" ", index);
findFirstNotOf = line.find_first_not_of(" ", index);
// Add element
if (find > findFirstNotOf) {
elements.push_back(line.substr(index, find - findFirstNotOf));
index += 1 + find - findFirstNotOf;
}
// Skip double spaces
else {
index = findFirstNotOf;
}
}
VERIFY(!elements.empty(), "Font file did not find any columns");
return elements;
}
const std::string Font::findValue(const std::string& key, const std::vector<std::string>& columns)
{
size_t find = 0;
// Loop over columns
for (auto& column : columns) {
find = column.find(key + "=");
if (find != std::string::npos) {
return column.substr(key.length() + 1);
}
}
VERIFY(false, "Font file did not contain key '{}'", key);
return "";
}
// -----------------------------------------
FontManager::FontManager(s)
{
ruc::info("FontManager initialized");
}
FontManager::~FontManager()
{
}
void FontManager::add(const std::string& name, std::shared_ptr<Font> font)
{
// Construct (key, value) pair and insert it into the unordered_map
m_fontList.emplace(std::move(name), std::move(font));
}
std::shared_ptr<Font> FontManager::load(const std::string& name)
{
if (exists(name)) {
return get(name);
}
std::shared_ptr<Font> font = std::make_shared<Font>(name);
add(name, font);
return get(name);
}
std::shared_ptr<Font> FontManager::get(const std::string& name)
{
return exists(name) ? m_fontList.at(name) : nullptr;
}
bool FontManager::exists(const std::string& name)
{
return m_fontList.find(name) != m_fontList.end();
}
void FontManager::remove(const std::string& name)
{
if (exists(name)) {
m_fontList.erase(name);
}
}
void FontManager::remove(std::shared_ptr<Font> font)
{
if (exists(font->name())) {
m_fontList.erase(font->name());
}
}
} // namespace Inferno
void ruc::format::Formatter<glm::ivec2>::format(Builder& builder, glm::ivec2 value) const
{
return Formatter<std::vector<int32_t>>::format(builder, { value.x, value.y });
}
-89
View File
@@ -1,89 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstdint> // int32_t, uint32_t
#include <memory> // std::shared_ptr
#include <string> // std::string
#include <unordered_map> // std::unordered_map
#include <vector> // std::vector
#include "glm/ext/vector_int2.hpp" // glm::ivec2
#include "glm/ext/vector_uint2.hpp" // glm::uvec2
#include "ruc/format/format.h"
#include "ruc/singleton.h"
namespace Inferno {
class Texture;
struct Character {
glm::uvec2 position; // Position
glm::uvec2 size; // Width/height
glm::ivec2 offset; // Offset from baseline to left / top of glyph
uint32_t advance; // Amount to advance to next glyph
};
// -------------------------------------
class Font {
public:
Font(const std::string& name);
virtual ~Font() {}
inline std::string name() const { return m_name; }
inline uint32_t size() const { return m_size; }
inline std::shared_ptr<Texture> texture() const { return m_texture; }
inline std::shared_ptr<Character> get(unsigned char c) const { return m_characterList.at(c); }
inline std::shared_ptr<Character> operator[](unsigned char c) const { return m_characterList.at(c); }
private:
void parseFont(const std::string& font);
const std::string findAction(const std::string& line);
const std::vector<std::string> findColumns(const std::string& line);
const std::string findValue(const std::string& key, const std::vector<std::string>& columns);
std::string m_name;
uint32_t m_size;
std::shared_ptr<Texture> m_texture;
std::unordered_map<unsigned char, std::shared_ptr<Character>> m_characterList;
};
// -------------------------------------
class FontManager final : public ruc::Singleton<FontManager> {
public:
FontManager(s);
virtual ~FontManager();
void add(const std::string& name, std::shared_ptr<Font> font);
std::shared_ptr<Font> load(const std::string& name);
std::shared_ptr<Font> get(const std::string& name);
bool exists(const std::string& name);
void remove(const std::string& name);
void remove(std::shared_ptr<Font> font);
private:
std::unordered_map<std::string, std::shared_ptr<Font>> m_fontList;
};
} // namespace Inferno
template<>
struct ruc::format::Formatter<glm::ivec2> : Formatter<std::vector<int32_t>> {
void format(Builder& builder, glm::ivec2 value) const;
};
// FontManager fm;
// Font f = fm.load("path/to/font");
// Font f2("path/to/font");
// Character c = f['a'];
// Look into using signed distance fields for texture map generation ? anti-aliasing for text
// https://youtu.be/d8cfgcJR9Tk
+140 -2
View File
@@ -1,21 +1,159 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <cstdint> // uint8_t, uint32_t
#include "glad/glad.h"
#include "ruc/meta/assert.h"
#include "inferno/asset/texture.h"
#include "inferno/render/framebuffer.h"
namespace Inferno {
Framebuffer::Framebuffer()
std::shared_ptr<Framebuffer> Framebuffer::create(const Properties& properties)
{
VERIFY((properties.attachments.size() > 0 && !properties.renderToScreen) || properties.renderToScreen,
"cant have attachments on the default framebuffer");
auto result = std::shared_ptr<Framebuffer>(new Framebuffer(properties));
result->createTextures();
return result;
}
Framebuffer::~Framebuffer()
{
if (m_renderToScreen) {
return;
}
glDeleteFramebuffers(1, &m_id);
}
void Framebuffer::copyBuffer(std::shared_ptr<Framebuffer> from, std::shared_ptr<Framebuffer> to, uint32_t bits, uint32_t filter)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, from->m_id);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // write to default framebuffer
glBlitFramebuffer(0, 0, from->m_width, from->m_height, 0, 0, to->m_width, to->m_height, bits, filter);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// -----------------------------------------
void Framebuffer::bind() const
{
glBindFramebuffer(GL_FRAMEBUFFER, m_id);
}
void Framebuffer::unbind() const
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
bool Framebuffer::check() const
{
VERIFY(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE,
"malformed framebuffer: {:#x}", glCheckFramebufferStatus(GL_FRAMEBUFFER));
return true;
}
void Framebuffer::resize(uint32_t width, uint32_t height)
{
if (m_width == width && m_height == height) {
return;
}
m_width = width;
m_height = height;
createTextures();
}
// -----------------------------------------
void Framebuffer::createTextures()
{
if (m_renderToScreen) {
return;
}
if (m_id) {
glDeleteFramebuffers(1, &m_id);
m_textures.clear();
}
m_id = UINT_MAX;
glGenFramebuffers(1, &m_id);
bind();
auto it = m_attachments.begin();
m_colorAttachmentCount = 0;
size_t size = m_attachments.size();
m_textures.resize(size);
for (size_t i = 0; i < size; ++i) {
TypeProperties type = *(it + i);
if (type.type == Type::RGB8) {
// Set color attachment 0 out of 32
m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGB8, GL_RGB);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0);
m_colorAttachmentCount++;
continue;
}
if (type.type == Type::RGBA8) { // Color
// Set color attachment 0 out of 32
m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGBA8, GL_RGBA);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0);
m_colorAttachmentCount++;
continue;
}
if (type.type == Type::RGBA16F) {
// Set color attachment 0 out of 32
m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGBA16F, GL_RGBA, GL_FLOAT);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0);
m_colorAttachmentCount++;
continue;
}
if (type.type == Type::RGBA32F) {
// Set color attachment 0 out of 32
m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGBA32F, GL_RGBA, GL_FLOAT);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0);
m_colorAttachmentCount++;
continue;
}
// This combined texture is required for older GPUs
if (type.type == Type::Depth24Stencil8) { // Depth
m_textures[i] = (TextureFramebuffer::create(
"", m_width, m_height, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8));
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_textures[i]->id(), 0);
continue;
}
if (type.type == Type::Depth32F) {
// FIXME: This isnt a 32-bit float texture
m_textures[i] = (TextureFramebuffer::create(
"", m_width, m_height, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT));
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_textures[i]->id(), 0);
continue;
}
}
VERIFY(m_colorAttachmentCount <= 32, "maximum color attachments was exceeded: {}/32", m_colorAttachmentCount);
check();
unbind();
}
} // namespace Inferno
+92 -4
View File
@@ -1,17 +1,105 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstddef> // size_t
#include <cstdint> // uint8_t
#include <initializer_list>
#include <memory> // std::shared_ptr
#include <vector>
#include "glm/ext/vector_float4.hpp" // glm::vec4
#include "inferno/asset/texture.h"
namespace Inferno {
class Framebuffer {
class Framebuffer final { // Frame Buffer Object, FBO
public:
Framebuffer();
virtual ~Framebuffer();
enum Type : uint8_t {
None = 0,
// Color
RGB8 = 1,
RGBA8 = 2,
RGBA16F = 3,
RGBA32F = 4,
// Depth/stencil
Depth24Stencil8 = 5,
Depth32F = 6,
// Defaults
Color = RGBA8,
Depth = Depth24Stencil8,
};
struct TypeProperties {
TypeProperties() = default;
TypeProperties(Type type)
: type(type)
{
}
Type type;
};
struct Properties {
std::initializer_list<TypeProperties> attachments {};
bool renderToScreen { false }; // (dummy framebuffer)
uint32_t width { 1280 };
uint32_t height { 720 };
glm::vec4 clearColor { 1.0f, 0.0f, 1.0f, 1.0f }; // magenta
uint32_t clearBit { 0 };
};
~Framebuffer();
// Factory function
static std::shared_ptr<Framebuffer> create(const Properties& properties);
static void copyBuffer(std::shared_ptr<Framebuffer> from, std::shared_ptr<Framebuffer> to, uint32_t bits, uint32_t filter);
void bind() const;
void unbind() const;
bool check() const;
void resize(uint32_t width, uint32_t height);
uint8_t colorAttachmentCount() const { return m_colorAttachmentCount; }
uint32_t id() const { return m_id; }
uint32_t width() const { return m_width; }
uint32_t height() const { return m_height; }
uint32_t clearBit() const { return m_clearBit; }
glm::vec4 clearColor() const { return m_clearColor; }
const std::vector<TypeProperties>& attachments() const { return m_attachments; }
std::shared_ptr<TextureFramebuffer> texture(size_t index) const { return m_textures[index]; }
private:
Framebuffer(const Properties& properties)
: m_renderToScreen(properties.renderToScreen)
, m_width(properties.width)
, m_height(properties.height)
, m_clearBit(properties.clearBit)
, m_clearColor(properties.clearColor)
, m_attachments(properties.attachments)
{
}
void createTextures();
private:
bool m_renderToScreen { false };
uint8_t m_colorAttachmentCount { 1 };
uint32_t m_id { 0 };
uint32_t m_width { 0 };
uint32_t m_height { 0 };
uint32_t m_clearBit { 0 };
glm::vec4 m_clearColor;
std::vector<TypeProperties> m_attachments;
std::vector<std::shared_ptr<TextureFramebuffer>> m_textures;
};
} // namespace Inferno
-347
View File
@@ -1,347 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#if 0
#include <algorithm> // std::copy
#include <utility> // std::move
#include "nlohmann/json.hpp"
#include "ruc/meta/assert.h"
#include "inferno/io/file.h"
#include "inferno/io/gltffile.h"
#include "inferno/io/log.h"
#include "inferno/render/gltf.h"
#include "inferno/util/integer.h"
namespace Inferno {
Gltf::Gltf(const std::string& path)
: m_path(std::move(path))
{
auto gltf = GltfFile::read(path);
VERIFY(gltf.first, "Gltf model invalid JSON content '{}'", path);
json json = json::parse(gltf.first.get());
// Add binary data from .glb files
if (gltf.second) {
m_model.data.emplace(0, std::move(gltf.second));
}
// Properties
// Asset
// ---------------------------------
VERIFY(Json::hasProperty(json, "asset"), "GlTF model missing required property 'asset'");
auto asset = json["asset"];
VERIFY(asset.is_object(), "Gltf model invalid property type 'asset'");
parseAsset(&m_model.asset, asset);
// Scene
// ---------------------------------
if (Json::hasProperty(json, "scenes")) {
auto scenes = json["scenes"];
VERIFY(scenes.is_array(), "Gltf model invalid property type 'scenes'");
for (auto& [key, object] : scenes.items()) {
glTF::Scene scene;
parseScene(&scene, key, object);
m_model.scenes.emplace_back(std::move(scene));
}
}
// Node
// ---------------------------------
if (Json::hasProperty(json, "nodes")) {
auto nodes = json["nodes"];
VERIFY(nodes.is_array(), "Gltf model invalid property type 'nodes'");
for (auto& [key, object] : nodes.items()) {
glTF::Node node;
parseNode(&node, key, object);
m_model.nodes.emplace_back(std::move(node));
}
}
// Mesh
// ---------------------------------
if (Json::hasProperty(json, "meshes")) {
auto meshes = json["meshes"];
VERIFY(meshes.is_array(), "Gltf model invalid property type 'meshes'");
for (auto& [key, object] : meshes.items()) {
glTF::Mesh mesh;
parseMesh(&mesh, key, object);
m_model.meshes.emplace_back(std::move(mesh));
}
}
// Accessor
// ---------------------------------
if (Json::hasProperty(json, "accessors")) {
auto accessors = json["accessors"];
VERIFY(accessors.is_array(), "Gltf model invalid property type 'accessors'");
for (auto& [key, object] : accessors.items()) {
glTF::Accessor accessor;
parseAccessor(&accessor, key, object);
m_model.accessors.emplace_back(std::move(accessor));
}
}
// Bufferview
// ---------------------------------
if (Json::hasProperty(json, "bufferViews")) {
auto bufferViews = json["bufferViews"];
VERIFY(bufferViews.is_array(), "Gltf model invalid property type 'bufferViews'");
for (auto& [key, object] : bufferViews.items()) {
glTF::BufferView bufferView;
parseBufferView(&bufferView, key, object);
m_model.bufferViews.emplace_back(std::move(bufferView));
}
}
// Buffer
// ---------------------------------
if (Json::hasProperty(json, "buffers")) {
auto buffers = json["buffers"];
VERIFY(buffers.is_array(), "Gltf model invalid property type 'buffers'");
for (auto& [key, object] : buffers.items()) {
glTF::Buffer buffer;
parseBuffer(&buffer, key, object, &m_model.data);
m_model.buffers.emplace_back(std::move(buffer));
}
}
}
Gltf::~Gltf()
{
}
void Gltf::parseAsset(glTF::Asset* asset, const json& object)
{
auto copyright = Json::parseStringProperty(object, "copyright", false);
auto generator = Json::parseStringProperty(object, "generator", false);
auto version = Json::parseStringProperty(object, "version", true);
VERIFY(version, "GlTF model missing required property 'version'");
VERIFY(version.value().compare("2.0") == 0, "GlTF version unsupported '{}'", version.value());
auto minVersion = Json::parseStringProperty(object, "minVersion", false);
if (copyright) asset->copyright = copyright.value();
if (generator) asset->generator = generator.value();
if (version) asset->version = version.value();
if (minVersion) asset->minVersion = minVersion.value();
}
void Gltf::parseScene(glTF::Scene* scene, const std::string& key, const json& object)
{
auto nodes = Json::parseFloatArrayProperty(object, "nodes", false);
VERIFY(!nodes || nodes.value().size() > 0, "Gltf scene '{}' empty 'nodes' property", key);
auto name = Json::parseStringProperty(object, "name", false);
scene->name = name ? name.value() : key;
}
void Gltf::parseNode(glTF::Node* node, const std::string& key, const json& object)
{
auto camera = Json::parseUnsignedProperty(object, "camera", false);
auto children = Json::parseUnsignedArrayProperty(object, "children", false);
VERIFY(!children || children.value().size() > 0, "Gltf node '{}' empty property 'children'", key);
auto skin = Json::parseUnsignedProperty(object, "skin", false);
auto matrix = Json::parseFloatArrayProperty(object, "matrix", false);
VERIFY(!matrix || matrix.value().size() == 16, "Gltf node '{}' property 'matrix' invalid size", key);
auto mesh = Json::parseUnsignedProperty(object, "mesh", false);
auto rotation = Json::parseFloatArrayProperty(object, "rotation", false);
VERIFY(!rotation || rotation.value().size() == 4, "Gltf node '{}' property 'rotation' invalid size", key);
auto scale = Json::parseFloatArrayProperty(object, "scale", false);
VERIFY(!scale || scale.value().size() == 3, "Gltf node '{}' property 'scale' invalid size", key);
auto translation = Json::parseFloatArrayProperty(object, "translation", false);
VERIFY(!translation || translation.value().size() == 3, "Gltf node '{}' property 'translation' invalid size", key);
auto weights = Json::parseFloatArrayProperty(object, "weights", false);
VERIFY(!weights || weights.value().size() > 0, "Gltf node '{}' empty property 'weights'", key);
auto name = Json::parseStringProperty(object, "name", false);
if (camera) node->camera = camera.value();
if (children) node->children = children.value();
if (skin) node->skin = skin.value();
if (matrix) std::copy(matrix.value().begin(), matrix.value().end(), node->matrix.begin());
if (mesh) node->mesh = mesh.value();
if (rotation) std::copy(rotation.value().begin(), rotation.value().end(), node->rotation.begin());
if (scale) std::copy(scale.value().begin(), scale.value().end(), node->scale.begin());
if (translation) std::copy(translation.value().begin(), translation.value().end(), node->translation.begin());
if (weights) node->weights = weights.value();
node->name = name ? name.value() : key;
}
void Gltf::parsePrimitive(glTF::Primitive* primitive, const std::string& key, const json& object)
{
auto attributes = Json::parseUnsignedObjectProperty(object, "attributes", true);
VERIFY(attributes && attributes.value().size() > 0, "Gltf primitive '{}' empty property 'attributes'", key);
auto indices = Json::parseUnsignedProperty(object, "indices", false);
auto material = Json::parseUnsignedProperty(object, "material", false);
auto mode = Json::parseUnsignedProperty(object, "mode", false);
if (Json::hasProperty(object, "targets")) {
auto targets = object["targets"];
VERIFY(targets.is_array(), "Gltf primitive '{}' property 'targets' invalid type", key);
for (auto& targetObject : targets) {
std::map<std::string, uint32_t> target;
for (auto& [key, propertyValue] : targetObject.items()) {
auto value = Json::getPropertyValue<uint32_t>(propertyValue, json::value_t::number_unsigned);
if (value) target.emplace(std::move(key), value.value());
}
VERIFY(target.size() > 0, "Gltf primitive '{}' empty 'target' object", key);
primitive->targets.emplace_back(std::move(target));
}
}
if (attributes) primitive->attributes = attributes.value();
if (indices) primitive->indices = indices.value();
if (material) primitive->material = material.value();
if (mode) primitive->mode = static_cast<unsigned char>(mode.value());
}
void Gltf::parseMesh(glTF::Mesh* mesh, const std::string& key, const json& object)
{
VERIFY(Json::hasProperty(object, "primitives"), "Gltf mesh '{}' missing required property 'primitives'", key);
auto primitives = object["primitives"];
VERIFY(primitives.is_array(), "Gltf mesh '{}' property 'primitives' invalid type", key);
for (auto& primitiveObject : primitives) {
glTF::Primitive primitive;
parsePrimitive(&primitive, key, primitiveObject);
mesh->primitives.emplace_back(std::move(primitive));
}
auto weights = Json::parseFloatArrayProperty(object, "weights", false);
VERIFY(!weights || weights.value().size() > 0, "Gltf mesh '{}' empty property 'weights'", key);
auto name = Json::parseStringProperty(object, "name", false);
if (weights) mesh->weights = weights.value();
mesh->name = name ? name.value() : key;
}
void Gltf::parseAccessor(glTF::Accessor* accessor, const std::string& key, const json& object)
{
auto bufferView = Json::parseUnsignedProperty(object, "bufferView", false);
auto byteOffset = Json::parseUnsignedProperty(object, "byteOffset", false);
auto componentType = Json::parseUnsignedProperty(object, "componentType", true);
VERIFY(componentType, "Gltf accessor '{}' missing required property 'componentType'", key);
auto normalized = Json::parseBoolProperty(object, "normalized", false);
auto count = Json::parseUnsignedProperty(object, "count", true);
VERIFY(count, "Gltf accessor '{}' missing required property 'count'", key);
auto type = Json::parseStringProperty(object, "type", true);
VERIFY(type, "Gltf accessor '{}' missing required property 'type'", key);
auto max = Json::parseFloatArrayProperty(object, "max", false);
VERIFY(!max || max.value().size() > 0, "Gltf accessor '{}' empty property 'max'", key);
auto min = Json::parseFloatArrayProperty(object, "min", false);
VERIFY(!min || min.value().size() > 0, "Gltf accessor '{}' empty property 'min'", key);
auto name = Json::parseStringProperty(object, "name", false);
if (bufferView) accessor->bufferView = bufferView.value();
if (byteOffset) accessor->byteOffset = byteOffset.value();
if (normalized) accessor->normalized = normalized.value();
if (count) accessor->count = count.value();
if (type) accessor->type = type.value();
if (max) accessor->max = max.value();
if (min) accessor->min = min.value();
accessor->name = name ? name.value() : key;
}
void Gltf::parseBufferView(glTF::BufferView* bufferView, const std::string& key, const json& object)
{
auto buffer = Json::parseUnsignedProperty(object, "buffer", false);
VERIFY(buffer, "Gltf bufferView '{}' missing required property 'buffer'", key);
auto byteOffset = Json::parseUnsignedProperty(object, "byteOffset", false);
auto byteLength = Json::parseUnsignedProperty(object, "byteLength", true);
VERIFY(byteLength, "Gltf bufferView '{}' missing required property 'byteLength'", key);
auto byteStride = Json::parseUnsignedProperty(object, "byteStride", false);
auto target = Json::parseUnsignedProperty(object, "target", false);
auto name = Json::parseStringProperty(object, "name", false);
if (buffer) bufferView->buffer = buffer.value();
if (byteOffset) bufferView->byteOffset = byteOffset.value();
if (byteLength) bufferView->byteLength = byteLength.value();
if (byteStride) bufferView->byteStride = byteStride.value();
if (target) bufferView->target = target.value();
bufferView->name = name ? name.value() : key;
}
void Gltf::parseBuffer(glTF::Buffer* buffer, const std::string& key, const json& object, std::map<uint32_t, std::shared_ptr<char[]>>* data)
{
auto uri = Json::parseStringProperty(object, "uri", false);
// Load external binary data
if (uri) {
VERIFY(uri.value().find("data:", 0) == std::string::npos, "GltfFile embedded binary data is unsupported");
data->emplace(std::stou(key), File::raw("assets/gltf/" + uri.value()));
}
auto byteLength = Json::parseUnsignedProperty(object, "byteLength", true);
VERIFY(byteLength, "Gltf buffer '{}' missing required property 'byteLength'", key);
auto name = Json::parseStringProperty(object, "name", false);
if (uri) buffer->uri = uri.value();
if (byteLength) buffer->byteLength = byteLength.value();
buffer->name = name ? name.value() : key;
}
} // namespace Inferno
#endif
-176
View File
@@ -1,176 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#if 0
#include <cstdint> // uint32_t
#include <memory> // std::shared_ptr
#include <string> // std::string
#include <unordered_map> // std::unordered_map
#include <vector> // std::vector
#include "ruc/singleton.h"
#include "inferno/util/json.h"
#define GLTF_TYPE_SCALAR 1
#define GLTF_TYPE_VEC2 2
#define GLTF_TYPE_VEC3 3
#define GLTF_TYPE_VEC4 4
#define GLTF_TYPE_MAT2 8
#define GLTF_TYPE_MAT3 12
#define GLTF_TYPE_MAT4 16
#define GLTF_COMPONENT_TYPE_BYTE 5120
#define GLTF_COMPONENT_TYPE_UNSIGNED_BYTE 5121
#define GLTF_COMPONENT_TYPE_SHORT 5122
#define GLTF_COMPONENT_TYPE_UNSIGNED_SHORT 5123
#define GLTF_COMPONENT_TYPE_INT 5124
#define GLTF_COMPONENT_TYPE_UNSIGNED_INT 5125
#define GLTF_COMPONENT_TYPE_FLOAT 5126
#define GLTF_TARGET_ARRAY_BUFFER 34962
#define GLTF_TARGET_ELEMENT_ARRAY_BUFFER 34963
namespace Inferno {
namespace glTF {
// Type specifications
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#objects
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#asset
struct Asset {
std::string copyright;
std::string generator;
std::string version; // Required
std::string minVersion;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes
struct Scene {
std::vector<uint32_t> nodes;
std::string name;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy
struct Node {
uint32_t camera;
std::vector<uint32_t> children;
uint32_t skin;
std::array<float, 16> matrix { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; // Identity matrix
uint32_t mesh;
std::array<float, 4> rotation { 0, 0, 0, 1 };
std::array<float, 3> scale { 1, 1, 1 };
std::array<float, 3> translation { 0, 0, 0 };
std::vector<float> weights;
std::string name;
};
struct Primitive {
std::map<std::string, uint32_t> attributes; // Required
uint32_t indices;
uint32_t material;
unsigned char mode { 4 };
std::vector<std::map<std::string, uint32_t>> targets;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#meshes
struct Mesh {
std::vector<Primitive> primitives; // Required
std::vector<float> weights;
std::string name;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#accessors
struct Accessor {
uint32_t bufferView;
uint32_t byteOffset { 0 };
uint32_t componentType; // Required
bool normalized { false };
uint32_t count; // Required
std::string type; // Required
std::vector<float> max;
std::vector<float> min;
std::string name;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#buffers-and-buffer-views
struct BufferView {
uint32_t buffer; // Required
uint32_t byteOffset { 0 };
uint32_t byteLength; // Required
uint32_t byteStride;
uint32_t target;
std::string name;
};
struct Buffer {
std::string uri;
uint32_t byteLength; // Required
std::string name;
};
struct Model {
Asset asset;
std::vector<Scene> scenes;
std::vector<Node> nodes;
std::vector<Mesh> meshes;
std::vector<Accessor> accessors;
std::vector<BufferView> bufferViews;
std::vector<Buffer> buffers;
std::map<uint32_t, std::shared_ptr<char[]>> data;
};
} // namespace glTF
// -----------------------------------------
class Gltf {
public:
Gltf(const std::string& path);
virtual ~Gltf();
inline const glTF::Model& model() const { return m_model; }
private:
static void parseAsset(glTF::Asset* asset, const json& object);
static void parseScene(glTF::Scene* scene, const std::string& key, const json& object);
static void parseNode(glTF::Node* node, const std::string& key, const json& object);
static void parsePrimitive(glTF::Primitive* primitive, const std::string& key, const json& object);
static void parseMesh(glTF::Mesh* mesh, const std::string& key, const json& object);
static void parseAccessor(glTF::Accessor* accessor, const std::string& key, const json& object);
static void parseBufferView(glTF::BufferView* bufferView, const std::string& key, const json& object);
static void parseBuffer(glTF::Buffer* buffer, const std::string& key, const json& object, std::map<uint32_t, std::shared_ptr<char[]>>* data);
std::string m_path;
glTF::Model m_model;
};
// -----------------------------------------
class GltfManager final : ruc::Singleton<GltfManager> {
public:
GltfManager(s) {}
virtual ~GltfManager() {}
void add(const std::string& path, std::shared_ptr<Gltf> gltf);
std::shared_ptr<Gltf> load(const std::string& path);
std::shared_ptr<Gltf> get(const std::string& path);
bool exists(const std::string& path);
void remove(const std::string& path);
void remove(std::shared_ptr<Gltf> gltf);
private:
std::unordered_map<std::string, std::shared_ptr<Gltf>> m_gltfList;
};
} // namespace Inferno
#endif
+96
View File
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstdint> // int32_t, uint32_t
#include <memory> // std::shared_ptr
#include "glad/glad.h"
#include "ruc/format/log.h"
#include "ruc/meta/assert.h"
#include "inferno/render/buffer.h"
#include "inferno/render/render-command.h"
namespace Inferno {
void RenderCommand::initialize()
{
setDepthTest(true);
// Enable transparency
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
ruc::info("RenderCommand initialized");
}
void RenderCommand::destroy()
{
}
void RenderCommand::clearBit(uint32_t bits)
{
// GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT
glClear(bits);
}
void RenderCommand::clearColor(const glm::vec4& color)
{
glClearColor(color.r, color.g, color.b, color.a);
}
void RenderCommand::drawIndexed(std::shared_ptr<VertexArray> vertexArray, uint32_t indexCount)
{
uint32_t count = indexCount ? indexCount : vertexArray->indexBuffer()->count();
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
}
void RenderCommand::setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height)
{
glViewport(x, y, width, height);
}
void RenderCommand::setDepthTest(bool enabled)
{
// Set z-buffer / depth buffer
enabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
}
void RenderCommand::setColorAttachmentCount(uint32_t count)
{
static constexpr uint32_t colorAttachments[] = {
GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2,
GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5,
GL_COLOR_ATTACHMENT6, GL_COLOR_ATTACHMENT7, GL_COLOR_ATTACHMENT8,
GL_COLOR_ATTACHMENT9, GL_COLOR_ATTACHMENT10, GL_COLOR_ATTACHMENT11,
GL_COLOR_ATTACHMENT12, GL_COLOR_ATTACHMENT13, GL_COLOR_ATTACHMENT14,
GL_COLOR_ATTACHMENT15, GL_COLOR_ATTACHMENT16, GL_COLOR_ATTACHMENT17,
GL_COLOR_ATTACHMENT18, GL_COLOR_ATTACHMENT19, GL_COLOR_ATTACHMENT20,
GL_COLOR_ATTACHMENT21, GL_COLOR_ATTACHMENT22, GL_COLOR_ATTACHMENT23,
GL_COLOR_ATTACHMENT24, GL_COLOR_ATTACHMENT25, GL_COLOR_ATTACHMENT26,
GL_COLOR_ATTACHMENT27, GL_COLOR_ATTACHMENT28, GL_COLOR_ATTACHMENT29,
GL_COLOR_ATTACHMENT30, GL_COLOR_ATTACHMENT31
};
static constexpr uint32_t maxCount = sizeof(colorAttachments) / sizeof(colorAttachments[0]);
VERIFY(count > 0 && count <= maxCount, "incorrect colorbuffer count: {}/{}", count, maxCount);
glDrawBuffers(static_cast<int32_t>(count), colorAttachments); // Multiple Render Targets (MRT)
}
bool RenderCommand::depthTest()
{
unsigned char depthTest = GL_FALSE;
glGetBooleanv(GL_DEPTH_TEST, &depthTest);
return depthTest == GL_TRUE;
}
int32_t RenderCommand::textureUnitAmount()
{
int32_t amount = 0;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &amount);
return amount;
}
} // namespace Inferno
+35
View File
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstdint> // int32_t, uint32_t
#include <memory> // std::shadred_ptr
#include "glm/ext/vector_float4.hpp" // glm::vec4
namespace Inferno {
class VertexArray;
class RenderCommand {
public:
static void initialize();
static void destroy();
static void clearBit(uint32_t bits);
static void clearColor(const glm::vec4& color);
static void drawIndexed(std::shared_ptr<VertexArray> vertexArray, uint32_t indexCount = 0);
static void setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height);
static void setDepthTest(bool enabled);
static void setColorAttachmentCount(uint32_t count);
static bool depthTest();
static int32_t textureUnitAmount();
};
} // namespace Inferno
+459 -251
View File
@@ -1,231 +1,240 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <algorithm> // std::min
#include <utility> // std::move
#include <algorithm> // std::copy, std::min
#include <span>
#include "glad/glad.h"
#include "glm/ext/vector_float4.hpp" // glm::vec4
#include "ruc/format/log.h"
#include "inferno/asset/asset-manager.h"
#include "inferno/asset/shader.h"
#include "inferno/asset/texture.h"
#include "inferno/component/transformcomponent.h"
#include "inferno/render/buffer.h"
#include "inferno/render/render-command.h"
#include "inferno/render/renderer.h"
#include "inferno/render/shader.h"
#include "inferno/render/texture.h"
namespace Inferno {
void RenderCommand::initialize()
{
setDepthTest(true);
// Enable transparency
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
ruc::info("RenderCommand initialized");
}
void RenderCommand::destroy()
template<typename T>
void Renderer<T>::beginScene(glm::mat4, glm::mat4)
{
}
void RenderCommand::clear()
template<typename T>
void Renderer<T>::endScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void RenderCommand::clearColor(const glm::vec4& color)
{
glClearColor(color.r, color.g, color.b, color.a);
}
void RenderCommand::drawIndexed(const VertexArray& vertexArray, uint32_t indexCount)
{
uint32_t count = indexCount ? indexCount : vertexArray.getIndexBuffer()->getCount();
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
}
void RenderCommand::setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height)
{
glViewport(x, y, width, height);
}
void RenderCommand::setDepthTest(bool enabled)
{
// Set z-buffer / depth buffer
enabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
}
bool RenderCommand::depthTest()
{
unsigned char depthTest = GL_FALSE;
glGetBooleanv(GL_DEPTH_TEST, &depthTest);
return depthTest == GL_TRUE;
}
int32_t RenderCommand::textureUnitAmount()
{
int32_t amount = 0;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &amount);
return amount;
nextBatch();
}
// -----------------------------------------
uint32_t Renderer::m_supportedTextureUnitPerBatch = 0;
void Renderer::initialize()
template<typename T>
void Renderer<T>::initialize()
{
// Get amount of texture units supported by the GPU
uint32_t constTextureUnitCount = textureUnitPerBatch;
uint32_t gpuTextureUnitCount = RenderCommand::textureUnitAmount();
m_supportedTextureUnitPerBatch = std::min(constTextureUnitCount, gpuTextureUnitCount);
m_maxSupportedTextureSlots = std::min(maxTextureSlots, gpuTextureUnitCount);
// Texture unit 0 is reserved for no texture
m_textureUnits[0] = nullptr;
m_textureSlots[0] = nullptr;
// Create texture unit samplers
int32_t samplers[textureUnitPerBatch];
for (uint32_t i = 0; i < textureUnitPerBatch; i++) {
int32_t samplers[maxTextureSlots];
for (uint32_t i = 0; i < maxTextureSlots; i++) {
samplers[i] = i;
}
// Create shader
loadShader();
m_shader->bind();
m_shader->setInt("u_textures", samplers, textureUnitPerBatch);
m_shader->setInt("u_textures", samplers, maxTextureSlots);
m_shader->unbind();
// Create vertex array
m_vertexArray = std::make_shared<VertexArray>();
createElementBuffer();
}
void Renderer::destroy()
template<typename T>
void Renderer<T>::destroy()
{
}
uint32_t Renderer::addTextureUnit(std::shared_ptr<Texture> texture)
template<typename T>
uint32_t Renderer<T>::addTextureUnit(std::shared_ptr<Texture> texture)
{
if (texture == nullptr) {
return 0;
}
// Create a new batch if the texture unit limit has been reached
if (m_textureUnitIndex >= m_supportedTextureUnitPerBatch) {
if (m_textureSlotIndex >= m_maxSupportedTextureSlots) {
nextBatch();
}
// If texure was already added
for (uint32_t i = 1; i < m_textureUnitIndex; i++) {
if (m_textureUnits[i] == texture) {
for (uint32_t i = 1; i < m_textureSlotIndex; i++) {
if (m_textureSlots[i] == texture) {
return i;
}
}
// Add texture
uint32_t textureUnitIndex = m_textureUnitIndex;
m_textureUnits[textureUnitIndex] = texture;
m_textureUnitIndex++;
uint32_t textureSlotIndex = m_textureSlotIndex;
m_textureSlots[textureSlotIndex] = texture;
m_textureSlotIndex++;
return textureUnitIndex;
return textureSlotIndex;
}
void Renderer::bind()
template<typename T>
void Renderer<T>::bind()
{
m_shader->bind();
for (uint32_t i = 1; i < m_textureUnitIndex; i++) {
m_textureUnits[i]->bind(i);
for (uint32_t i = 1; i < m_textureSlotIndex; i++) {
m_textureSlots[i]->bind(i);
}
m_vertexArray->bind();
}
void Renderer::unbind()
template<typename T>
void Renderer<T>::unbind()
{
m_vertexArray->unbind();
for (uint32_t i = 1; i < m_textureUnitIndex; i++) {
m_textureUnits[i]->unbind();
for (uint32_t i = 1; i < m_textureSlotIndex; i++) {
m_textureSlots[i]->unbind();
}
m_shader->unbind();
}
template<typename T>
void Renderer<T>::createElementBuffer()
{
// ---------------------------------
// CPU
// Generate indices
uint32_t* elements = new uint32_t[maxElements];
uint32_t offset = 0;
for (uint32_t i = 0; i < maxElements; i += elementPerQuad) {
elements[i + 0] = offset + 0;
elements[i + 1] = offset + 1;
elements[i + 2] = offset + 2;
elements[i + 3] = offset + 2;
elements[i + 4] = offset + 3;
elements[i + 5] = offset + 0;
offset += vertexPerQuad;
}
// ---------------------------------
// GPU
// Create index buffer
auto indexBuffer = std::make_shared<IndexBuffer>(elements, sizeof(uint32_t) * maxElements);
m_vertexArray->setIndexBuffer(indexBuffer);
delete[] elements;
}
template<typename T>
void Renderer<T>::flush()
{
if (m_vertexIndex == 0 || m_elementIndex == 0) {
return;
}
// Upload index data to GPU
uploadElementBuffer();
// Upload vertex data to GPU
m_vertexArray->at(0)->uploadData(m_vertexBufferBase.get(), m_vertexIndex * sizeof(T));
bind();
// Render
bool depthTest = RenderCommand::depthTest();
RenderCommand::setDepthTest(m_enableDepthBuffer);
RenderCommand::setColorAttachmentCount(m_colorAttachmentCount);
RenderCommand::drawIndexed(m_vertexArray, m_elementIndex);
RenderCommand::setDepthTest(depthTest);
unbind();
}
template<typename T>
void Renderer<T>::startBatch()
{
m_vertexIndex = 0;
m_elementIndex = 0;
m_vertexBufferPtr = m_vertexBufferBase.get();
m_textureSlotIndex = 1;
}
template<typename T>
void Renderer<T>::nextBatch()
{
flush();
startBatch();
}
// -----------------------------------------
Renderer2D::Renderer2D(s)
{
Renderer::initialize();
// CPU
// ---------------------------------
// Create array for storing quads vertices
m_vertexBufferBase = std::make_unique<QuadVertex[]>(vertexCount);
m_vertexBufferPtr = m_vertexBufferBase.get();
// Set default quad vertex positions
m_vertexPositions[0] = { -0.5f, -0.5f, 0.0f, 1.0f };
m_vertexPositions[1] = { 0.5f, -0.5f, 0.0f, 1.0f };
m_vertexPositions[2] = { 0.5f, 0.5f, 0.0f, 1.0f };
m_vertexPositions[3] = { -0.5f, 0.5f, 0.0f, 1.0f };
// Generate indices
uint32_t* indices = new uint32_t[indexCount];
uint32_t offset = 0;
for (uint32_t i = 0; i < indexCount; i += indexPerQuad) {
indices[i + 0] = offset + 0;
indices[i + 1] = offset + 1;
indices[i + 2] = offset + 2;
indices[i + 3] = offset + 2;
indices[i + 4] = offset + 3;
indices[i + 5] = offset + 0;
offset += vertexPerQuad;
}
// GPU
// ---------------------------------
// Create vertex buffer
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(QuadVertex) * vertexCount);
vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec4, "a_color" },
{ BufferElementType::Vec2, "a_textureCoordinates" },
{ BufferElementType::Float, "a_textureIndex" },
});
m_vertexArray->addVertexBuffer(vertexBuffer);
// Create index buffer
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * indexCount);
m_vertexArray->setIndexBuffer(indexBuffer);
delete[] indices;
ruc::info("Renderer2D initialized");
Renderer2D::initialize();
}
Renderer2D::~Renderer2D()
{
Renderer::destroy();
}
void Renderer2D::beginScene(glm::mat4 cameraProjectionView)
void Renderer2D::initialize()
{
m_shader->bind();
m_shader->setFloat("u_projectionView", cameraProjectionView);
m_shader->unbind();
}
Renderer::initialize();
void Renderer2D::endScene()
{
nextBatch();
// ---------------------------------
// CPU
// Create array for storing quads vertices
m_vertexBufferBase = std::make_unique<QuadVertex[]>(maxVertices);
m_vertexBufferPtr = m_vertexBufferBase.get();
// Set default quad vertex positions
m_vertexPositions[0] = { -1.0f, -1.0f, 0.0f, 1.0f };
m_vertexPositions[1] = { 1.0f, -1.0f, 0.0f, 1.0f };
m_vertexPositions[2] = { 1.0f, 1.0f, 0.0f, 1.0f };
m_vertexPositions[3] = { -1.0f, 1.0f, 0.0f, 1.0f };
// ---------------------------------
// GPU
m_enableDepthBuffer = false;
// Create vertex buffer
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(QuadVertex) * maxVertices);
vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec4, "a_color" },
{ BufferElementType::Vec2, "a_textureCoordinates" },
{ BufferElementType::Uint, "a_textureIndex" },
});
m_vertexArray->addVertexBuffer(vertexBuffer);
ruc::info("Renderer2D initialized");
}
void Renderer2D::drawQuad(const TransformComponent& transform, glm::vec4 color)
@@ -246,7 +255,7 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::vec4 color,
void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture)
{
// Create a new batch if the quad limit has been reached
if (m_quadIndex >= quadCount) {
if (m_vertexIndex + vertexPerQuad > maxVertices || m_elementIndex + elementPerQuad > maxElements) {
nextBatch();
}
@@ -264,90 +273,168 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color,
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
m_vertexBufferPtr->color = color[i];
m_vertexBufferPtr->textureCoordinates = textureCoordinates[i];
m_vertexBufferPtr->textureIndex = (float)textureUnitIndex;
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
m_quadIndex++;
m_vertexIndex += vertexPerQuad;
m_elementIndex += elementPerQuad;
}
void Renderer2D::loadShader()
{
m_shader = ShaderManager::the().load("assets/glsl/batch-quad");
}
void Renderer2D::flush()
{
if (m_quadIndex == 0) {
return;
}
// Upload vertex data to GPU
m_vertexArray->getVertexBuffers().at(0)->uploadData(
m_vertexBufferBase.get(),
m_quadIndex * vertexPerQuad * sizeof(QuadVertex));
bind();
// Render
RenderCommand::drawIndexed(*m_vertexArray, m_quadIndex * indexPerQuad);
unbind();
}
void Renderer2D::startBatch()
{
m_quadIndex = 0;
m_vertexBufferPtr = m_vertexBufferBase.get();
m_textureUnitIndex = 1;
}
void Renderer2D::nextBatch()
{
flush();
startBatch();
m_shader = AssetManager::the().load<Shader>("assets/glsl/batch-2d");
}
// -----------------------------------------
RendererCharacter::RendererCharacter(s)
RendererCubemap::RendererCubemap(s)
{
RendererCubemap::initialize();
}
RendererCubemap::~RendererCubemap()
{
}
void RendererCubemap::initialize()
{
Renderer::initialize();
// CPU
// ---------------------------------
// CPU
// Create array for storing quads vertices
m_vertexBufferBase = std::make_unique<CharacterVertex[]>(vertexCount);
m_vertexBufferBase = std::make_unique<CubemapVertex[]>(maxVertices);
m_vertexBufferPtr = m_vertexBufferBase.get();
// Generate indices
// Set default cubemap vertex positions
uint32_t* indices = new uint32_t[indexCount];
// Back face - v
m_vertexPositions[0] = { 0.5f, -0.5f, 0.5f, 1.0f };
m_vertexPositions[1] = { -0.5f, -0.5f, 0.5f, 1.0f };
m_vertexPositions[2] = { -0.5f, 0.5f, 0.5f, 1.0f };
m_vertexPositions[3] = { 0.5f, 0.5f, 0.5f, 1.0f };
uint32_t offset = 0;
for (uint32_t i = 0; i < indexCount; i += indexPerQuad) {
indices[i + 0] = offset + 0;
indices[i + 1] = offset + 1;
indices[i + 2] = offset + 2;
indices[i + 3] = offset + 2;
indices[i + 4] = offset + 3;
indices[i + 5] = offset + 0;
// Left face - v
m_vertexPositions[7] = { -0.5f, 0.5f, 0.5f, 1.0f };
m_vertexPositions[6] = { -0.5f, 0.5f, -0.5f, 1.0f };
m_vertexPositions[5] = { -0.5f, -0.5f, -0.5f, 1.0f };
m_vertexPositions[4] = { -0.5f, -0.5f, 0.5f, 1.0f };
offset += vertexPerQuad;
}
// Right face - v
m_vertexPositions[8] = { 0.5f, -0.5f, -0.5f, 1.0f };
m_vertexPositions[9] = { 0.5f, -0.5f, 0.5f, 1.0f };
m_vertexPositions[10] = { 0.5f, 0.5f, 0.5f, 1.0f };
m_vertexPositions[11] = { 0.5f, 0.5f, -0.5f, 1.0f };
// Front face - v
m_vertexPositions[12] = { -0.5f, -0.5f, -0.5f, 1.0f };
m_vertexPositions[13] = { 0.5f, -0.5f, -0.5f, 1.0f };
m_vertexPositions[14] = { 0.5f, 0.5f, -0.5f, 1.0f };
m_vertexPositions[15] = { -0.5f, 0.5f, -0.5f, 1.0f };
// Top face
m_vertexPositions[16] = { -0.5f, 0.5f, -0.5f, 1.0f };
m_vertexPositions[17] = { 0.5f, 0.5f, -0.5f, 1.0f };
m_vertexPositions[18] = { 0.5f, 0.5f, 0.5f, 1.0f };
m_vertexPositions[19] = { -0.5f, 0.5f, 0.5f, 1.0f };
// Bottom face
m_vertexPositions[20] = { -0.5f, -0.5f, -0.5f, 1.0f };
m_vertexPositions[21] = { -0.5f, -0.5f, 0.5f, 1.0f };
m_vertexPositions[22] = { 0.5f, -0.5f, 0.5f, 1.0f };
m_vertexPositions[23] = { 0.5f, -0.5f, -0.5f, 1.0f };
// GPU
// ---------------------------------
// GPU
m_enableDepthBuffer = false;
// Create vertex buffer
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(CharacterVertex) * vertexCount);
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(CubemapVertex) * maxVertices);
vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec4, "a_color" },
{ BufferElementType::Uint, "a_textureIndex" },
});
m_vertexArray->addVertexBuffer(vertexBuffer);
ruc::info("RendererCubemap initialized");
}
void RendererCubemap::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
{
// We want the skybox fixed in position, so only retain the rotation and scale.
// Set the translation of the camera's view matrix to 0, meaning:
// x x x 0
// x x x 0
// x x x 0
// 0 0 0 1
cameraView = glm::mat4(glm::mat3(cameraView));
m_shader->bind();
m_shader->setFloat("u_projectionView", cameraProjection * cameraView);
m_shader->unbind();
}
void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture)
{
drawCubemap(transform, glm::mat4(color, color, color, color), texture);
}
void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture)
{
// Create a new batch if the quad limit has been reached
if (m_vertexIndex + (vertexPerQuad * quadPerCube) > maxVertices
|| m_elementIndex + (elementPerQuad * quadPerCube) > maxElements) {
nextBatch();
}
uint32_t textureUnitIndex = addTextureUnit(texture);
// Add the quads 4 vertices, 6 times, once per cube side
for (uint32_t i = 0; i < vertexPerQuad * quadPerCube; i++) {
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
m_vertexBufferPtr->color = color[i % 4];
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
m_vertexIndex += vertexPerQuad * quadPerCube;
m_elementIndex += elementPerQuad * quadPerCube;
}
void RendererCubemap::loadShader()
{
m_shader = AssetManager::the().load<Shader>("assets/glsl/batch-cubemap");
}
// -----------------------------------------
RendererFont::RendererFont(s)
{
Renderer::initialize();
// ---------------------------------
// CPU
// Create array for storing quads vertices
m_vertexBufferBase = std::make_unique<SymbolVertex[]>(maxVertices);
m_vertexBufferPtr = m_vertexBufferBase.get();
// ---------------------------------
// GPU
m_enableDepthBuffer = false;
// Create vertex buffer
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(SymbolVertex) * maxVertices);
vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec4, "a_color" },
{ BufferElementType::Vec2, "a_textureCoordinates" },
{ BufferElementType::Float, "a_textureIndex" },
{ BufferElementType::Uint, "a_textureIndex" },
{ BufferElementType::Float, "a_width" },
{ BufferElementType::Float, "a_edge" },
{ BufferElementType::Float, "a_borderWidth" },
@@ -357,32 +444,17 @@ RendererCharacter::RendererCharacter(s)
});
m_vertexArray->addVertexBuffer(vertexBuffer);
// Create index buffer
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * indexCount);
m_vertexArray->setIndexBuffer(indexBuffer);
delete[] indices;
ruc::info("RendererCharacter initialized");
ruc::info("RendererFont initialized");
}
RendererCharacter::~RendererCharacter()
{
Renderer::destroy();
}
void RendererCharacter::beginScene()
RendererFont::~RendererFont()
{
}
void RendererCharacter::endScene()
{
nextBatch();
}
void RendererCharacter::drawCharacter(std::array<CharacterVertex, vertexPerQuad>& characterQuad, std::shared_ptr<Texture> texture)
void RendererFont::drawSymbol(std::array<SymbolVertex, vertexPerQuad>& symbolQuad, std::shared_ptr<Texture> texture)
{
// Create a new batch if the quad limit has been reached
if (m_quadIndex >= quadCount) {
if (m_vertexIndex + vertexPerQuad > maxVertices || m_elementIndex + elementPerQuad > maxElements) {
nextBatch();
}
@@ -390,63 +462,199 @@ void RendererCharacter::drawCharacter(std::array<CharacterVertex, vertexPerQuad>
// Add the quads 4 vertices
for (uint32_t i = 0; i < vertexPerQuad; i++) {
m_vertexBufferPtr->quad.position = characterQuad[i].quad.position;
m_vertexBufferPtr->quad.color = characterQuad[i].quad.color;
m_vertexBufferPtr->quad.textureCoordinates = characterQuad[i].quad.textureCoordinates;
m_vertexBufferPtr->quad.textureIndex = (float)textureUnitIndex;
m_vertexBufferPtr->quad.position = symbolQuad[i].quad.position;
m_vertexBufferPtr->quad.color = symbolQuad[i].quad.color;
m_vertexBufferPtr->quad.textureCoordinates = symbolQuad[i].quad.textureCoordinates;
m_vertexBufferPtr->quad.textureIndex = textureUnitIndex;
m_vertexBufferPtr->width = characterQuad[i].width;
m_vertexBufferPtr->edge = characterQuad[i].edge;
m_vertexBufferPtr->borderWidth = characterQuad[i].borderWidth;
m_vertexBufferPtr->borderEdge = characterQuad[i].borderEdge;
m_vertexBufferPtr->borderColor = characterQuad[i].borderColor;
m_vertexBufferPtr->offset = characterQuad[i].offset;
m_vertexBufferPtr->width = symbolQuad[i].width;
m_vertexBufferPtr->edge = symbolQuad[i].edge;
m_vertexBufferPtr->borderWidth = symbolQuad[i].borderWidth;
m_vertexBufferPtr->borderEdge = symbolQuad[i].borderEdge;
m_vertexBufferPtr->borderColor = symbolQuad[i].borderColor;
m_vertexBufferPtr->offset = symbolQuad[i].offset;
m_vertexBufferPtr++;
}
m_quadIndex++;
m_vertexIndex += vertexPerQuad;
m_elementIndex += elementPerQuad;
}
void RendererCharacter::loadShader()
void RendererFont::loadShader()
{
m_shader = ShaderManager::the().load("assets/glsl/batch-font");
m_shader = AssetManager::the().load<Shader>("assets/glsl/batch-font");
}
void RendererCharacter::flush()
// -----------------------------------------
Renderer3D::Renderer3D(s)
{
if (m_quadIndex == 0) {
return;
}
Renderer::initialize();
// Upload vertex data to GPU
m_vertexArray->getVertexBuffers().at(0)->uploadData(
m_vertexBufferBase.get(),
m_quadIndex * vertexPerQuad * sizeof(CharacterVertex));
// ---------------------------------
// CPU
bind();
// Render
bool depthTest = RenderCommand::depthTest();
RenderCommand::setDepthTest(false);
RenderCommand::drawIndexed(*m_vertexArray, m_quadIndex * indexPerQuad);
RenderCommand::setDepthTest(depthTest);
unbind();
}
void RendererCharacter::startBatch()
{
m_quadIndex = 0;
// Create array for storing quads vertices
m_vertexBufferBase = std::make_unique<Vertex[]>(maxVertices);
m_vertexBufferPtr = m_vertexBufferBase.get();
m_textureUnitIndex = 1;
// ---------------------------------
// GPU
m_enableDepthBuffer = true;
m_colorAttachmentCount = 3;
// Create vertex buffer
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(Vertex) * maxVertices);
vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec3, "a_normal" },
{ BufferElementType::Vec4, "a_color" },
{ BufferElementType::Vec2, "a_textureCoordinates" },
{ BufferElementType::Uint, "a_textureIndex" },
});
m_vertexArray->addVertexBuffer(vertexBuffer);
ruc::info("Renderer3D initialized");
}
void RendererCharacter::nextBatch()
Renderer3D::~Renderer3D()
{
flush();
startBatch();
}
void Renderer3D::drawModel(std::span<const Vertex> vertices, std::span<const uint32_t> elements, const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture)
{
// ruc::error("drawModel");
VERIFY(vertices.size() <= maxVertices, "model vertices too big for buffer, {}/{}", vertices.size(), maxVertices);
VERIFY(elements.size() <= maxElements, "model elements too big for buffer, {}/{}", elements.size(), maxElements);
// Create a new batch if the quad limit has been reached
if (m_vertexIndex + vertices.size() > maxVertices || m_elementIndex + elements.size() > maxElements) {
nextBatch();
}
uint32_t textureUnitIndex = addTextureUnit(texture);
// Add the vertices
glm::mat3 normalMatrix = glm::mat3(glm::transpose(glm::inverse(transform.transform)));
for (const auto& vertex : vertices) {
m_vertexBufferPtr->position = transform.transform * glm::vec4(vertex.position, 1.0f);
m_vertexBufferPtr->normal = normalMatrix * vertex.normal; // take non-uniform scaling into consideration
m_vertexBufferPtr->color = color;
m_vertexBufferPtr->textureCoordinates = vertex.textureCoordinates;
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
// Copy element indices to the element buffer
for (const auto& element : elements) {
// Indices are referenced relative to vertices[0], if there are multiple models in a batch,
// then the indices need to be offset by the total amount of vertices
*m_elementBufferPtr++ = element + m_vertexIndex;
}
m_vertexIndex += vertices.size();
m_elementIndex += elements.size();
}
void Renderer3D::createElementBuffer()
{
// ---------------------------------
// CPU
// Create array for storing quads vertices
m_elementBufferBase = std::make_unique<uint32_t[]>(maxElements);
m_elementBufferPtr = m_elementBufferBase.get();
// ---------------------------------
// GPU
// Create index buffer
auto indexBuffer = std::make_shared<IndexBuffer>(m_elementBufferBase.get(), sizeof(uint32_t) * maxElements);
m_vertexArray->setIndexBuffer(indexBuffer);
}
void Renderer3D::uploadElementBuffer()
{
m_vertexArray->indexBuffer()->uploadData(m_elementBufferBase.get(), m_elementIndex * sizeof(uint32_t));
}
void Renderer3D::loadShader()
{
m_shader = AssetManager::the().load<Shader>("assets/glsl/batch-3d");
}
void Renderer3D::startBatch()
{
Renderer<Vertex>::startBatch();
m_elementBufferPtr = m_elementBufferBase.get();
}
// -----------------------------------------
RendererPostProcess::RendererPostProcess(s)
{
Renderer2D::initialize();
ruc::info("RendererPostProcess initialized");
}
RendererPostProcess::~RendererPostProcess()
{
}
void RendererPostProcess::drawQuad(const TransformComponent& transform, std::shared_ptr<Texture> albedo, std::shared_ptr<Texture> position, std::shared_ptr<Texture> normal)
{
nextBatch();
constexpr glm::vec2 textureCoordinates[] = {
{ 0.0f, 0.0f },
{ 1.0f, 0.0f },
{ 1.0f, 1.0f },
{ 0.0f, 1.0f }
};
uint32_t textureUnitIndex = addTextureUnit(albedo);
addTextureUnit(position);
addTextureUnit(normal);
// Add the quads 4 vertices
for (uint32_t i = 0; i < vertexPerQuad; i++) {
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
m_vertexBufferPtr->textureCoordinates = textureCoordinates[i];
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
m_vertexIndex += vertexPerQuad;
m_elementIndex += elementPerQuad;
}
void RendererPostProcess::loadShader()
{
m_shader = AssetManager::the().load<Shader>("assets/glsl/post-process");
}
// -----------------------------------------
RendererLightCube::RendererLightCube(s)
{
RendererCubemap::initialize();
m_enableDepthBuffer = true;
ruc::info("RendererLightCube initialized");
}
RendererLightCube::~RendererLightCube()
{
}
void RendererLightCube::loadShader()
{
m_shader = AssetManager::the().load<Shader>("assets/glsl/lightsource");
}
} // namespace Inferno
+172 -73
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
@@ -8,6 +8,7 @@
#include <cstdint> // int32_t, uint32_t
#include <memory> // std::shared_ptr, std::unique_ptr, std::make_shared, std::make_unique
#include <span>
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
#include "glm/ext/vector_float2.hpp" // glm::vec2
@@ -15,22 +16,28 @@
#include "glm/ext/vector_float4.hpp" // glm::vec4
#include "ruc/singleton.h"
#include "inferno/component/transformcomponent.h"
#include "inferno/asset/shader.h"
namespace Inferno {
class Shader;
class Texture;
class TransformComponent;
class VertexArray;
struct QuadVertex {
glm::vec3 position { 0.0f, 0.0f, 0.0f };
glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f };
glm::vec2 textureCoordinates { 0.0f, 0.0f };
float textureIndex = 0; // @Todo get int to pass to fragment correctly
glm::vec3 position { 0.0f };
glm::vec4 color { 1.0f };
glm::vec2 textureCoordinates { 0.0f };
uint32_t textureIndex { 0 };
};
struct CharacterVertex {
struct CubemapVertex {
glm::vec3 position { 0.0f };
glm::vec4 color { 1.0f };
uint32_t textureIndex { 0 };
};
struct SymbolVertex {
QuadVertex quad;
// Font
@@ -39,39 +46,45 @@ struct CharacterVertex {
// Outline
float borderWidth = 0.7f;
float borderEdge = 0.1f;
glm::vec4 borderColor { 1.0f, 1.0f, 1.0f, 1.0f };
glm::vec4 borderColor { 1.0f };
// Dropshadow
float offset = 0.0f;
};
// -------------------------------------
class RenderCommand {
public:
static void initialize();
static void destroy();
static void clear();
static void clearColor(const glm::vec4& color);
static void drawIndexed(const VertexArray& vertexArray, uint32_t indexCount = 0);
static void setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height);
static void setDepthTest(bool enabled);
static bool depthTest();
static int32_t textureUnitAmount();
struct Vertex {
glm::vec3 position { 0.0f };
glm::vec3 normal { 1.0f };
glm::vec4 color { 1.0f };
glm::vec2 textureCoordinates { 0.0f };
uint32_t textureIndex { 0 };
};
// -------------------------------------
template<typename T>
class Renderer {
public:
static const uint32_t vertexPerQuad = 4;
static const uint32_t indexPerQuad = 6;
static const uint32_t textureUnitPerBatch = 32;
static constexpr const uint32_t vertexPerFace = 3;
static constexpr const uint32_t elementPerFace = 3;
static constexpr const uint32_t vertexPerQuad = 4;
static constexpr const uint32_t elementPerQuad = 6;
// When to start a new batch
static constexpr const uint32_t maxVertices = 60000;
static constexpr const uint32_t maxElements = 60000;
static constexpr const uint32_t maxTextureSlots = 32;
public:
virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView);
virtual void endScene();
void setEnableDepthBuffer(bool state) { m_enableDepthBuffer = state; }
uint32_t shaderID() const { return m_shader->id(); }
protected:
Renderer() {}
virtual ~Renderer() { destroy(); };
void initialize();
void destroy();
@@ -80,27 +93,43 @@ protected:
void bind();
void unbind();
virtual void loadShader() = 0;
virtual void flush() = 0;
virtual void startBatch() = 0;
virtual void nextBatch() = 0;
uint32_t m_quadIndex = 0;
virtual void createElementBuffer();
virtual void uploadElementBuffer() {}
virtual void loadShader() = 0;
virtual void flush();
virtual void startBatch();
virtual void nextBatch();
protected:
// CPU quad vertices
uint32_t m_vertexIndex { 0 };
uint32_t m_elementIndex { 0 };
std::unique_ptr<T[]> m_vertexBufferBase { nullptr };
T* m_vertexBufferPtr { nullptr };
// Texture units
static uint32_t m_supportedTextureUnitPerBatch;
uint32_t m_textureUnitIndex = 1;
std::array<std::shared_ptr<Texture>, textureUnitPerBatch> m_textureUnits;
static inline uint32_t m_maxSupportedTextureSlots { 0 };
uint32_t m_textureSlotIndex { 1 };
std::array<std::shared_ptr<Texture>, maxTextureSlots> m_textureSlots;
// GPU objects
bool m_enableDepthBuffer { true };
uint32_t m_colorAttachmentCount { 1 };
std::shared_ptr<Shader> m_shader;
std::shared_ptr<VertexArray> m_vertexArray;
};
// TOOD:
// v Deduplicate flush()
// v Add bool for disabling depth buffer
// - Add Size for uploadData (this is prob not needed, we got T already)
// - Decide if its worth to remove template<T> from Renderer, just cast vertexBufferPtr before usage
// -------------------------------------
class Renderer2D final
: public Renderer
class Renderer2D
: public Renderer<QuadVertex>
, public ruc::Singleton<Renderer2D> {
public:
Renderer2D(s);
@@ -108,61 +137,131 @@ public:
using Singleton<Renderer2D>::destroy;
static const uint32_t quadCount = 1000;
static const uint32_t vertexCount = quadCount * vertexPerQuad;
static const uint32_t indexCount = quadCount * indexPerQuad;
void beginScene(glm::mat4 cameraProjectionView);
void endScene();
void drawQuad(const TransformComponent& transform, glm::vec4 color);
void drawQuad(const TransformComponent& transform, glm::mat4 color);
void drawQuad(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture);
void drawQuad(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture);
private:
void loadShader() override;
void flush() override;
void startBatch() override;
void nextBatch() override;
protected:
Renderer2D() {} // Needed for derived classes
// CPU quad vertices
std::unique_ptr<QuadVertex[]> m_vertexBufferBase;
QuadVertex* m_vertexBufferPtr { nullptr };
void initialize();
// Default quad vertex positions
glm::vec4 m_vertexPositions[vertexPerQuad];
private:
virtual void loadShader() override;
};
// -------------------------------------
class RendererCubemap
: public Renderer<CubemapVertex>
, public ruc::Singleton<RendererCubemap> {
public:
static constexpr const uint32_t quadPerCube = 6;
public:
RendererCubemap(s);
virtual ~RendererCubemap();
using Singleton<RendererCubemap>::destroy;
virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) override;
void drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture);
void drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture);
protected:
RendererCubemap() {} // Needed for derived classes
void initialize();
private:
virtual void loadShader() override;
// Default cubemap vertex positions
glm::vec4 m_vertexPositions[vertexPerQuad * quadPerCube];
};
// -------------------------------------
class RendererCharacter final
: public Renderer
, public ruc::Singleton<RendererCharacter> {
class RendererFont final
: public Renderer<SymbolVertex>
, public ruc::Singleton<RendererFont> {
public:
RendererCharacter(s);
virtual ~RendererCharacter();
RendererFont(s);
virtual ~RendererFont();
using Singleton<RendererCharacter>::destroy;
using Singleton<RendererFont>::destroy;
static const uint32_t quadCount = 1000;
static const uint32_t vertexCount = quadCount * vertexPerQuad;
static const uint32_t indexCount = quadCount * indexPerQuad;
void beginScene();
void endScene();
void drawCharacter(std::array<CharacterVertex, vertexPerQuad>& characterQuad, std::shared_ptr<Texture> texture);
void drawSymbol(std::array<SymbolVertex, vertexPerQuad>& symbolQuad, std::shared_ptr<Texture> texture);
private:
void loadShader() override;
void flush() override;
void startBatch() override;
void nextBatch() override;
};
// CPU quad vertices
std::unique_ptr<CharacterVertex[]> m_vertexBufferBase;
CharacterVertex* m_vertexBufferPtr { nullptr };
// -------------------------------------
class Renderer3D final
: public Renderer<Vertex>
, public ruc::Singleton<Renderer3D> {
public:
Renderer3D(s);
virtual ~Renderer3D();
using Singleton<Renderer3D>::destroy;
void drawModel(std::span<const Vertex> vertices, std::span<const uint32_t> indices, const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture);
private:
void createElementBuffer() override;
void uploadElementBuffer() override;
void loadShader() override;
void startBatch() override;
private:
// CPU element vertices
std::unique_ptr<uint32_t[]> m_elementBufferBase { nullptr };
uint32_t* m_elementBufferPtr { nullptr };
};
// -----------------------------------------
class RendererPostProcess final
: public Renderer2D
, public ruc::Singleton<RendererPostProcess> {
public:
using Singleton<RendererPostProcess>::s;
using Singleton<RendererPostProcess>::the;
using Singleton<RendererPostProcess>::destroy;
RendererPostProcess(s);
virtual ~RendererPostProcess();
void drawQuad(const TransformComponent& transform, std::shared_ptr<Texture> albedo, std::shared_ptr<Texture> position, std::shared_ptr<Texture> normal);
private:
virtual void loadShader() override;
};
// -----------------------------------------
class RendererLightCube final
: public RendererCubemap
, public ruc::Singleton<RendererLightCube> {
public:
using Singleton<RendererLightCube>::s;
using Singleton<RendererLightCube>::the;
using Singleton<RendererLightCube>::destroy;
RendererLightCube(s);
virtual ~RendererLightCube();
void beginScene(glm::mat4, glm::mat4) override {}
private:
virtual void loadShader() override;
};
} // namespace Inferno
@@ -0,0 +1,195 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstdint> // int32_t, uint8_t, uintt32_t
#include "glad/glad.h"
#include "glm/ext/matrix_float2x2.hpp" // glm::mat2
#include "glm/ext/matrix_float3x3.hpp" // glm::mat3
#include "glm/ext/vector_float4.hpp" // glm::vec4
#include "inferno/render/buffer.h"
#include "inferno/render/shader-storage-buffer.h"
namespace Inferno {
ShaderStorageBuffer::ShaderStorageBuffer(s)
{
// Get maximum uniformbuffer bindings the GPU supports
int32_t maxBindingPoints = 0;
glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxBindingPoints);
m_maxBindingPoints = static_cast<uint8_t>(maxBindingPoints);
}
ShaderStorageBuffer::~ShaderStorageBuffer()
{
}
// -----------------------------------------
// https://stackoverflow.com/questions/56512216#answer-56513136
void ShaderStorageBuffer::setLayout(std::string_view blockName, uint8_t bindingPoint, uint32_t shaderID)
{
VERIFY(bindingPoint < m_maxBindingPoints,
"shader storage buffer exceeded binding points: {}/{}", bindingPoint, m_maxBindingPoints);
if (!exists(blockName)) {
m_blocks[blockName.data()] = {};
}
BufferBlock& block = m_blocks[blockName.data()];
block.bindingPoint = bindingPoint;
block.memberOffsets.clear();
glBindBuffer(GL_SHADER_STORAGE_BUFFER, block.id);
// Get the shader block index
uint32_t resourceIndex = glGetProgramResourceIndex(shaderID, GL_SHADER_STORAGE_BLOCK, blockName.data());
VERIFY(resourceIndex != GL_INVALID_INDEX, "block doesnt exist in shader: {}::{}", blockName, shaderID);
// Get the amount of member variables
uint32_t prop = GL_NUM_ACTIVE_VARIABLES;
int32_t memberCount;
glGetProgramResourceiv(shaderID, GL_SHADER_STORAGE_BLOCK, resourceIndex, 1, &prop, 1, nullptr, &memberCount);
// Get the indices of the members
prop = GL_ACTIVE_VARIABLES;
std::vector<int32_t> members(memberCount);
glGetProgramResourceiv(shaderID, GL_SHADER_STORAGE_BLOCK, resourceIndex, 1, &prop, (int32_t)members.size(), nullptr, members.data());
// Reserve memory for the names of the members
int32_t memberNameSize;
glGetProgramInterfaceiv(shaderID, GL_BUFFER_VARIABLE, GL_MAX_NAME_LENGTH, &memberNameSize);
std::vector<char> memberNameBuffer(memberNameSize);
int32_t lastOffset = 0;
int32_t lastType = 0;
for (int32_t i = 0; i < memberCount; i++) {
// Get name of buffer variable
int32_t stringLength;
glGetProgramResourceName(shaderID, GL_BUFFER_VARIABLE, members[i], memberNameSize, &stringLength, memberNameBuffer.data());
std::string memberName = std::string(memberNameBuffer.begin(), memberNameBuffer.begin() + stringLength);
// Get the other data needed for computing
uint32_t props[7] = {
GL_OFFSET, // 0
GL_TYPE, // 1
GL_ARRAY_SIZE, // 2
GL_ARRAY_STRIDE, // 3
GL_MATRIX_STRIDE, // 4
GL_TOP_LEVEL_ARRAY_SIZE, // 5
GL_TOP_LEVEL_ARRAY_STRIDE, // 6
};
int32_t params[7];
glGetProgramResourceiv(shaderID, GL_BUFFER_VARIABLE, members[i], 7, props, 7, nullptr, params);
// ruc::error("{}", memberName);
// ruc::error("\n Offset: {}, type: {:#x}, arraySize: {}, arrayStride: {},\n matrixStride: {}, top level size: {}, top level stride: {}\n",
// params[0], params[1], params[2], params[3], params[4], params[5], params[6]);
// Array of structs
if (params[5] != 1 && params[6 != 0]) {
size_t bracketOpen = memberName.find_first_of('[');
size_t bracketClose = memberName.find_first_of(']');
std::string memberNameBegin = memberName.substr(0, bracketOpen); // name: myArray[0].member -> myArray
std::string memberNameMember = memberName.substr(bracketClose + 1); // name: myArray[0].member -> .member
for (int32_t j = 0; j < params[5]; ++j) {
lastOffset = params[0] + (j * params[6]); // calc offset
lastType = params[1];
// Only add the member variant the first time its encountered
if (j == 0 && block.memberOffsets.find(memberNameBegin) == block.memberOffsets.end()) {
block.memberOffsets.emplace(memberNameBegin, lastOffset); // name: myArray
}
// Only add the index variant the first time its encountered
std::string memberNameIndex = memberNameBegin + "[" + std::to_string(j) + "]"; // name: myArray -> myArray[i]
if (block.memberOffsets.find(memberNameIndex) == block.memberOffsets.end()) {
block.memberOffsets.emplace(memberNameIndex, lastOffset);
}
block.memberOffsets.emplace(memberNameIndex + memberNameMember, // name: myArray -> myArray[i].member
lastOffset);
}
}
// Array of primitives
else if (params[2] != 1 && params[3] != 0) {
std::string memberNameBegin = memberName.substr(0, memberName.find_first_of('[')); // name: myArray[0] -> myArray
for (int32_t j = 0; j < params[2]; ++j) {
lastOffset = params[0] + (j * params[3]); // calc offset
lastType = params[1];
// Only add the member variant the first time its encountered
if (j == 0) {
block.memberOffsets.emplace(memberNameBegin, lastOffset); // name: myArray
}
block.memberOffsets.emplace(memberNameBegin + "[" + std::to_string(j) + "]", // name: myArray -> myArray[i]
lastOffset);
}
}
// Matrix case
else if (params[4] != 0) {
lastType = params[1];
lastOffset = params[0];
block.memberOffsets.emplace(memberName, params[0]);
}
else {
lastType = params[1];
lastOffset = params[0];
block.memberOffsets.emplace(memberName, params[0]);
}
}
block.size = lastOffset + BufferElement::getGLTypeSize(lastType);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
// Results
// for (auto [k, v] : block.memberOffsets) {
// ruc::error("{}:{}", k, v);
// }
}
void ShaderStorageBuffer::create(std::string_view blockName)
{
VERIFY(exists(blockName), "shader storage buffer block doesnt exist");
BufferBlock& block = m_blocks[blockName.data()];
if (block.id != 0) {
glDeleteBuffers(1, &block.id);
}
// Allocate buffer
block.id = UINT_MAX;
glGenBuffers(1, &block.id);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, block.id);
glBufferData(GL_SHADER_STORAGE_BUFFER, block.size, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
// Bind buffer to binding point
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, block.bindingPoint, block.id);
}
void ShaderStorageBuffer::setValue(std::string_view blockName, std::string_view member, bool value)
{
setValue(blockName, member, static_cast<uint32_t>(value), sizeof(uint32_t));
}
void ShaderStorageBuffer::setValue(std::string_view blockName, std::string_view member, glm::mat2 value)
{
setValue(blockName, member, static_cast<glm::mat4>(value), sizeof(glm::vec4) * 2);
}
void ShaderStorageBuffer::setValue(std::string_view blockName, std::string_view member, glm::mat3 value)
{
setValue(blockName, member, static_cast<glm::mat4>(value), sizeof(glm::vec4) * 3);
}
} // namespace Inferno
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstddef> // size_t
#include <cstdint> // uint8_t, uint32_t
#include <string_view>
#include <unordered_map>
#include "glad/glad.h"
#include "glm/ext/matrix_float2x2.hpp" // glm::mat2
#include "glm/ext/matrix_float3x3.hpp" // glm::mat3
#include "ruc/singleton.h"
#define CHECK_SET_CALL(blockName, member) \
VERIFY(exists(blockName), "shader storage buffer block doesnt exist: {}", blockName); \
const BufferBlock& block = m_blocks[blockName.data()]; \
VERIFY(block.memberOffsets.find(member.data()) != block.memberOffsets.end(), \
"shader storage buffer member doesnt exist: {}", member);
namespace Inferno {
struct BufferBlock {
uint32_t id { 0 };
uint32_t size { 0 };
uint8_t bindingPoint { 0 };
std::unordered_map<std::string, uint32_t> memberOffsets {};
};
class ShaderStorageBuffer final : public ruc::Singleton<ShaderStorageBuffer> { // Shader Storage Buffer Object, SSBO
public:
ShaderStorageBuffer(s);
virtual ~ShaderStorageBuffer();
void setLayout(std::string_view blockName, uint8_t bindingPoint, uint32_t shaderID);
void create(std::string_view blockName);
bool exists(std::string_view blockName) const { return m_blocks.find(blockName.data()) != m_blocks.end(); }
template<typename T> // Capture value by reference, instead of decaying to pointer
void setValue(std::string_view blockName, std::string_view member, T&& value, size_t size = 0)
{
CHECK_SET_CALL(blockName, member);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, block.id);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, block.memberOffsets.at(member.data()), (size) ? size : sizeof(T), &value);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}
// Exceptions:
void setValue(std::string_view blockName, std::string_view member, bool value);
void setValue(std::string_view blockName, std::string_view member, glm::mat2 value);
void setValue(std::string_view blockName, std::string_view member, glm::mat3 value);
private:
uint8_t m_maxBindingPoints { 0 };
std::unordered_map<std::string, BufferBlock> m_blocks;
};
} // namespace Inferno
+24
View File
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "glm/ext/vector_float3.hpp" // glm::vec3
namespace Inferno {
// Shader storage block layouts, using std430 memory layout rules
#define MAX_DIRECTIONAL_LIGHTS 4
struct alignas(16) DirectionalLightBlock {
alignas(16) glm::vec3 direction { 0 };
alignas(16) glm::vec3 ambient { 0 };
alignas(16) glm::vec3 diffuse { 0 };
alignas(16) glm::vec3 specular { 0 };
};
} // namespace Inferno
-278
View File
@@ -1,278 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <utility> // std::move
#include <vector> // std::vector
#include "glad/glad.h"
#include "glm/gtc/type_ptr.hpp" // glm::value_ptr
#include "ruc/file.h"
#include "ruc/format/log.h"
#include "ruc/meta/assert.h"
#include "inferno/core.h"
#include "inferno/render/shader.h"
namespace Inferno {
Shader::Shader(const std::string& name)
: m_name(std::move(name))
, m_id(0)
{
// Get file contents
std::string vertexSrc = ruc::File(name + ".vert").data();
std::string fragmentSrc = ruc::File(name + ".frag").data();
// Compile shaders
uint32_t vertexID = compileShader(GL_VERTEX_SHADER, vertexSrc.c_str());
uint32_t fragmentID = compileShader(GL_FRAGMENT_SHADER, fragmentSrc.c_str());
// Link shaders
if (vertexID > 0 && fragmentID > 0) {
m_id = linkShader(vertexID, fragmentID);
}
// Clear resources
else if (vertexID > 0)
glDeleteShader(vertexID);
else if (fragmentID > 0)
glDeleteShader(fragmentID);
}
Shader::~Shader()
{
if (m_id > 0) {
glDeleteProgram(m_id);
m_id = 0;
}
}
int32_t Shader::findUniform(const std::string& name) const
{
int32_t location = glGetUniformLocation(m_id, name.c_str());
VERIFY(location != -1, "Shader could not find uniform '{}'", name);
return location;
}
void Shader::setInt(const std::string& name, int value)
{
// Set uniform int
glUniform1i(findUniform(name), value);
}
void Shader::setInt(const std::string& name, int* values, uint32_t count)
{
// Set uniform int array
glUniform1iv(findUniform(name), count, values);
}
void Shader::setFloat(const std::string& name, float value) const
{
// Set uniform float
glUniform1f(findUniform(name), value);
}
void Shader::setFloat(const std::string& name, float v1, float v2, float v3, float v4) const
{
// Set uniform vec4 data
glUniform4f(findUniform(name), v1, v2, v3, v4);
}
void Shader::setFloat(const std::string& name, glm::vec2 value) const
{
// Set uniform vec2 data
glUniform2f(findUniform(name), value.x, value.y);
}
void Shader::setFloat(const std::string& name, glm::vec3 value) const
{
// Set uniform vec3 data
glUniform3f(findUniform(name), value.x, value.y, value.z);
}
void Shader::setFloat(const std::string& name, glm::vec4 value) const
{
// Set uniform vec4 data
glUniform4f(findUniform(name), value.x, value.y, value.z, value.w);
}
void Shader::setFloat(const std::string& name, glm::mat3 matrix) const
{
// Set uniform mat3 data
glUniformMatrix3fv(findUniform(name), 1, GL_FALSE, glm::value_ptr(matrix));
}
void Shader::setFloat(const std::string& name, glm::mat4 matrix) const
{
// Set uniform mat4 data
glUniformMatrix4fv(findUniform(name), 1, GL_FALSE, glm::value_ptr(matrix));
}
void Shader::bind() const
{
glUseProgram(m_id);
}
void Shader::unbind() const
{
glUseProgram(0);
}
uint32_t Shader::compileShader(int32_t type, const char* source) const
{
// Create new shader
uint32_t shader = 0;
shader = glCreateShader(type);
// Attach shader source to shader object
glShaderSource(shader, 1, &source, nullptr);
// Compile shader
glCompileShader(shader);
// Check compilation status
if (checkStatus(shader) == GL_TRUE) {
return shader;
}
// On fail
glDeleteShader(shader);
return 0;
}
uint32_t Shader::linkShader(uint32_t vertex, uint32_t fragment) const
{
// Create new shader program
uint32_t shaderProgram = 0;
shaderProgram = glCreateProgram();
// Attach both shaders to the shader program
glAttachShader(shaderProgram, vertex);
glAttachShader(shaderProgram, fragment);
// Setup vertex attributes
glBindAttribLocation(shaderProgram, 0, "a_position");
// Link the shaders
glLinkProgram(shaderProgram);
// Clear resources
glDeleteShader(vertex);
glDeleteShader(fragment);
// Check linking status
if (checkStatus(shaderProgram, true) == GL_TRUE) {
return shaderProgram;
}
// On fail
glDeleteProgram(shaderProgram);
return 0;
}
int32_t Shader::checkStatus(uint32_t check, bool isProgram) const
{
int32_t success;
int32_t maxLength = 0;
std::vector<char> infoLog;
// Get the compilation/linking status
!isProgram
? glGetShaderiv(check, GL_COMPILE_STATUS, &success)
: glGetProgramiv(check, GL_LINK_STATUS, &success);
if (success != GL_TRUE) {
// Get max length of the log including \0 terminator
!isProgram
? glGetShaderiv(check, GL_INFO_LOG_LENGTH, &maxLength)
: glGetProgramiv(check, GL_INFO_LOG_LENGTH, &maxLength);
// Reserve data for the log
infoLog.reserve(maxLength);
// Retrieve the error message
!isProgram
? glGetShaderInfoLog(check, maxLength, nullptr, &infoLog[0])
: glGetProgramInfoLog(check, maxLength, nullptr, &infoLog[0]);
ruc::warn("Shader {}", infoLog.data());
}
VERIFY(success == GL_TRUE, "Shader program creation failed!");
return success;
}
// -----------------------------------------
ShaderManager::ShaderManager(s)
{
ruc::info("ShaderManager initialized");
}
ShaderManager::~ShaderManager()
{
}
void ShaderManager::add(const std::string& name, std::shared_ptr<Shader> shader)
{
// Construct (key, value) pair and insert it into the unordered_map
m_shaderList.emplace(std::move(name), std::move(shader));
}
std::shared_ptr<Shader> ShaderManager::load(const std::string& name)
{
if (exists(name)) {
return get(name);
}
std::shared_ptr<Shader> shader = std::make_shared<Shader>(name);
add(name, shader);
return get(name);
}
std::shared_ptr<Shader> ShaderManager::load(const std::string& vertexSource,
const std::string& fragmentSource)
{
std::string name = computeName(vertexSource, fragmentSource);
return load(name);
}
std::shared_ptr<Shader> ShaderManager::get(const std::string& name)
{
return exists(name) ? m_shaderList.at(name) : nullptr;
}
bool ShaderManager::exists(const std::string& name)
{
return m_shaderList.find(name) != m_shaderList.end();
}
void ShaderManager::remove(const std::string& name)
{
if (exists(name)) {
m_shaderList.erase(name);
}
}
void ShaderManager::remove(std::shared_ptr<Shader> shader)
{
if (exists(shader->name())) {
m_shaderList.erase(shader->name());
}
}
std::string ShaderManager::computeName(const std::string& vertexSource,
const std::string& fragmentSource)
{
auto vertexPos = vertexSource.find_last_of('.');
auto fragmentPos = fragmentSource.find_last_of('.');
VERIFY(vertexPos != std::string::npos, "Shader did not have file extension: '{}'", vertexSource);
VERIFY(fragmentPos != std::string::npos, "Shader did not have file extension: '{}'", fragmentSource);
auto vertexName = vertexSource.substr(0, vertexPos);
auto fragmentName = vertexSource.substr(0, fragmentPos);
VERIFY(vertexName == fragmentName, "Shader names did not match: {} {}", vertexSource, fragmentSource);
return vertexName;
}
} // namespace Inferno
-77
View File
@@ -1,77 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstdint> // int32_t, uint32_t
#include <memory> // std::shared_ptr
#include <string> // std::string
#include <unordered_map> // std::unordered_map
#include "glm/glm.hpp"
#include "ruc/singleton.h"
namespace Inferno {
class Shader {
public:
Shader(const std::string& name);
virtual ~Shader();
int32_t findUniform(const std::string& name) const;
void setInt(const std::string& name, int value);
void setInt(const std::string& name, int* values, uint32_t count);
void setFloat(const std::string& name, float value) const;
void setFloat(const std::string& name, float v1, float v2, float v3, float v4) const;
void setFloat(const std::string& name, glm::vec2 value) const;
void setFloat(const std::string& name, glm::vec3 value) const;
void setFloat(const std::string& name, glm::vec4 value) const;
void setFloat(const std::string& name, glm::mat3 matrix) const;
void setFloat(const std::string& name, glm::mat4 matrix) const;
void bind() const;
void unbind() const;
inline std::string name() const { return m_name; }
inline uint32_t id() const { return m_id; }
protected:
uint32_t compileShader(int32_t type, const char* shaderSource) const;
uint32_t linkShader(uint32_t vertex, uint32_t fragment) const;
int32_t checkStatus(uint32_t check, bool isProgram = false) const;
private:
std::string m_name;
uint32_t m_id;
};
// -------------------------------------
class ShaderManager final : public ruc::Singleton<ShaderManager> {
public:
ShaderManager(s);
virtual ~ShaderManager();
void add(const std::string& name, std::shared_ptr<Shader> shader);
std::shared_ptr<Shader> load(const std::string& name);
std::shared_ptr<Shader> load(const std::string& vertexSource,
const std::string& fragmentSource);
std::shared_ptr<Shader> get(const std::string& name);
bool exists(const std::string& name);
void remove(const std::string& name);
void remove(std::shared_ptr<Shader> shader);
protected:
std::string computeName(const std::string& vertexSource,
const std::string& fragmentSource);
private:
std::unordered_map<std::string, std::shared_ptr<Shader>> m_shaderList;
};
} // namespace Inferno
-163
View File
@@ -1,163 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <climits> // UINT_MAX
#include <memory> // std::shared_ptr
#include <utility> // std::move
#include "glad/glad.h"
#include "ruc/format/log.h"
#include "ruc/meta/assert.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb/stb_image.h"
#include "inferno/render/texture.h"
namespace Inferno {
Texture::Texture(const std::string& path)
: m_path(std::move(path))
{
int width;
int height;
int channels;
// Load image data
stbi_set_flip_vertically_on_load(1);
unsigned char* data = stbi_load(path.c_str(), &width, &height, &channels, STBI_default);
VERIFY(data, "Failed to load image: '{}'", path);
m_width = width;
m_height = height;
if (channels == 4) {
m_internalFormat = GL_RGBA8;
m_dataFormat = GL_RGBA;
}
else if (channels == 3) {
m_internalFormat = GL_RGB8;
m_dataFormat = GL_RGB;
}
create(data);
// Clean resources
stbi_image_free(data);
}
Texture::~Texture()
{
glDeleteTextures(1, &m_id);
}
void Texture::bind(uint32_t unit) const
{
// Set active unit
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, m_id);
// Reset unit
glActiveTexture(GL_TEXTURE0);
}
void Texture::unbind() const
{
glBindTexture(GL_TEXTURE_2D, 0);
}
void Texture::create(unsigned char* data)
{
m_id = UINT_MAX;
// Create texture object
glGenTextures(1, &m_id);
// Bind texture object
glBindTexture(GL_TEXTURE_2D, m_id);
// Set unpacking of pixel data to byte-alignment,
// this prevents alignment issues when using a single byte for color
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Generate texture
glTexImage2D(
GL_TEXTURE_2D, // Texture target
0, // Midmap level, base starts at level 0
m_internalFormat, // Texture format
m_width, m_height, // Image width/height
0, // Always 0 (legacy)
m_dataFormat, // Texture source format
GL_UNSIGNED_BYTE, // Texture source datatype
data); // Image data
// Set the texture wrapping / filtering options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // X
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Y
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Minify
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Magnify
// Automatically generate all mipmap levels
glGenerateMipmap(GL_TEXTURE_2D);
// Unbind texture object
glBindTexture(GL_TEXTURE_2D, 0);
}
// -----------------------------------------
TextureManager::TextureManager(s)
{
ruc::info("TextureManager initialized");
}
TextureManager::~TextureManager()
{
}
void TextureManager::add(const std::string& path, std::shared_ptr<Texture> texture)
{
// Construct (key, value) pair and insert it into the unordered_map
m_textureList.emplace(std::move(path), std::move(texture));
}
std::shared_ptr<Texture> TextureManager::load(const std::string& path)
{
if (exists(path)) {
return get(path);
}
std::shared_ptr<Texture> texture = std::make_shared<Texture>(path);
add(path, texture);
return get(path);
}
std::shared_ptr<Texture> TextureManager::get(const std::string& path)
{
return exists(path) ? m_textureList.at(path) : nullptr;
}
bool TextureManager::exists(const std::string& path)
{
return m_textureList.find(path) != m_textureList.end();
}
void TextureManager::remove(const std::string& path)
{
if (exists(path)) {
m_textureList.erase(path);
}
}
void TextureManager::remove(std::shared_ptr<Texture> texture)
{
if (exists(texture->path())) {
m_textureList.erase(texture->path());
}
}
} // namespace Inferno
-64
View File
@@ -1,64 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstdint> // uint32_t
#include <memory> // std::shared_ptr
#include <string> // std::string
#include <unordered_map> // std::unordered_map
#include "ruc/singleton.h"
namespace Inferno {
class Texture {
public:
Texture(const std::string& path);
virtual ~Texture();
void bind(uint32_t unit = 0) const;
void unbind() const;
inline std::string path() const { return m_path; }
inline uint32_t width() const { return m_width; }
inline uint32_t height() const { return m_height; }
inline uint32_t id() const { return m_id; }
inline uint32_t internalFormat() const { return m_internalFormat; }
inline uint32_t dataFormat() const { return m_dataFormat; }
protected:
void create(unsigned char* data);
private:
std::string m_path;
uint32_t m_width;
uint32_t m_height;
uint32_t m_id;
uint32_t m_internalFormat;
uint32_t m_dataFormat;
};
// -------------------------------------
class TextureManager final : public ruc::Singleton<TextureManager> {
public:
TextureManager(s);
virtual ~TextureManager();
void add(const std::string& path, std::shared_ptr<Texture> texture);
std::shared_ptr<Texture> load(const std::string& path);
std::shared_ptr<Texture> get(const std::string& path);
bool exists(const std::string& path);
void remove(const std::string& path);
void remove(std::shared_ptr<Texture> texture);
private:
std::unordered_map<std::string, std::shared_ptr<Texture>> m_textureList;
};
} // namespace Inferno
+241
View File
@@ -0,0 +1,241 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint32_t, uint8_t
#include <string_view>
#include "glad/glad.h"
#include "glm/ext/matrix_float2x2.hpp" // glm::mat2
#include "glm/ext/matrix_float3x3.hpp" // glm::mat3
#include "glm/gtc/type_ptr.hpp" // glm::value_ptr
#include "ruc/meta/assert.h"
#include "inferno/render/buffer.h"
#include "inferno/render/uniformbuffer.h"
namespace Inferno {
Uniformbuffer::Uniformbuffer(s)
{
// Get maximum uniformbuffer bindings the GPU supports
int32_t maxBindingPoints = 0;
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxBindingPoints);
m_maxBindingPoints = static_cast<uint8_t>(maxBindingPoints);
}
Uniformbuffer::~Uniformbuffer()
{
for (const auto& [_, block] : m_blocks) {
glDeleteBuffers(1, &block.id);
}
m_blocks.clear();
}
// -----------------------------------------
void Uniformbuffer::setLayout(std::string_view blockName, const UniformbufferBlock& block)
{
VERIFY(block.size && block.bindingPoint && block.uniformLocations.size(),
"invalid uniformbuffer block definition: {}", blockName);
VERIFY(block.bindingPoint < m_maxBindingPoints,
"uniformbuffer exceeded binding points: {}/{}", block.bindingPoint, m_maxBindingPoints);
m_blocks[blockName] = block;
}
void Uniformbuffer::setLayout(std::string_view blockName, uint8_t bindingPoint, const BufferLayout& layout)
{
VERIFY(bindingPoint < m_maxBindingPoints,
"uniformbuffer exceeded binding points: {}/{}", bindingPoint, m_maxBindingPoints);
if (!exists(blockName)) {
m_blocks[blockName] = {};
}
UniformbufferBlock& block = m_blocks[blockName];
block.bindingPoint = bindingPoint;
block.uniformLocations.clear();
// Example block layout:
// - mat3
// - float
// - vec2
// - vec2
// - float
// Chunks, 4 slots, 4 bytes per slot
// [x][x][x][ ] #1
// [x][x][x][ ] #2
// [x][x][x][ ] #3
// [x][ ][x][x] #4
// [x][x][x][ ] #5
size_t chunk = 0;
uint8_t offset = 0;
for (auto it = layout.begin(); it != layout.end(); ++it) {
BufferElementType type = it->type();
const std::string& name = it->name();
// Calculate offset
switch (type) {
// Scalar 1
case BufferElementType::Bool:
case BufferElementType::Int:
case BufferElementType::Uint:
case BufferElementType::Float: {
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Jump
offset += 1;
break;
}
// Scalar 2
case BufferElementType::Bool2:
case BufferElementType::Int2:
case BufferElementType::Uint2:
case BufferElementType::Vec2: {
// Add padding
if (offset == 1) {
offset++;
}
if (offset == 3) {
offset = 0;
chunk++;
}
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Jump
offset += 2;
break;
}
// Scalar 3
case BufferElementType::Bool3:
case BufferElementType::Int3:
case BufferElementType::Uint3:
case BufferElementType::Vec3: {
// Add padding
if (offset != 0) {
offset = 0;
chunk++;
}
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Jump
offset += 3;
break;
}
// Scalar 4
case BufferElementType::Bool4:
case BufferElementType::Int4:
case BufferElementType::Uint4:
case BufferElementType::Vec4: {
// Add padding
if (offset != 0) {
offset = 0;
chunk++;
}
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Jump
offset += 4;
break;
}
// Array types
case BufferElementType::Mat2:
case BufferElementType::Mat3:
case BufferElementType::Mat4: {
// Add padding
if (offset != 0) {
offset = 0;
chunk++;
}
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Additional rows
if (type == BufferElementType::Mat2) {
chunk += 1;
}
else if (type == BufferElementType::Mat3) {
chunk += 2;
}
else {
chunk += 3;
}
// Jump
offset += 4;
break;
}
// TODO: Implement these types
case BufferElementType::Double:
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
case BufferElementType::MatDouble2:
case BufferElementType::MatDouble3:
case BufferElementType::MatDouble4:
VERIFY_NOT_REACHED();
case BufferElementType::None:
VERIFY_NOT_REACHED();
};
// Overflow slots to next chunk
if (offset > 3) {
offset = 0;
chunk++;
}
}
// Pad the end of the buffer
if (offset != 0) {
offset = 0;
chunk++;
}
block.size = chunk * 16;
}
void Uniformbuffer::create(std::string_view blockName)
{
VERIFY(exists(blockName), "uniformbuffer block doesnt exist");
UniformbufferBlock& block = m_blocks[blockName];
if (block.id != 0) {
glDeleteBuffers(1, &block.id);
}
// Allocate buffer
block.id = UINT_MAX;
glGenBuffers(1, &block.id);
glBindBuffer(GL_UNIFORM_BUFFER, block.id);
glBufferData(GL_UNIFORM_BUFFER, block.size, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// Bind buffer to binding point
glBindBufferBase(GL_UNIFORM_BUFFER, block.bindingPoint, block.id);
}
void Uniformbuffer::setValue(std::string_view blockName, std::string_view member, bool value)
{
setValue(blockName, member, static_cast<uint32_t>(value), sizeof(uint32_t));
}
void Uniformbuffer::setValue(std::string_view blockName, std::string_view member, glm::mat2 value)
{
setValue(blockName, member, static_cast<glm::mat4>(value), sizeof(glm::vec4) * 2);
}
void Uniformbuffer::setValue(std::string_view blockName, std::string_view member, glm::mat3 value)
{
setValue(blockName, member, static_cast<glm::mat4>(value), sizeof(glm::vec4) * 3);
}
} // namespace Inferno
+114
View File
@@ -0,0 +1,114 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstddef> // size_t
#include <cstdint> // uint8_t, uint32_t
#include <string>
#include <string_view>
#include <unordered_map>
#include "glad/glad.h"
#include "glm/ext/matrix_float2x2.hpp" // glm::mat2
#include "glm/ext/matrix_float3x3.hpp" // glm::mat3
#include "ruc/singleton.h"
#include "inferno/render/buffer.h"
#define CHECK_UNIFORM_SET_CALL(blockName, member) \
VERIFY(exists(blockName), "uniformbuffer block doesnt exist: {}", blockName); \
const UniformbufferBlock& block = m_blocks[blockName]; \
VERIFY(block.uniformLocations.find(member.data()) != block.uniformLocations.end(), \
"uniformbuffer member doesnt exist: {}", member);
namespace Inferno {
struct UniformbufferBlock {
uint32_t id { 0 };
uint32_t size { 0 };
uint8_t bindingPoint { 0 };
std::unordered_map<std::string, uint32_t> uniformLocations {};
};
class Uniformbuffer final : public ruc::Singleton<Uniformbuffer> { // Uniform Buffer Object, UBO
public:
Uniformbuffer(s);
~Uniformbuffer();
void setLayout(std::string_view blockName, const UniformbufferBlock& block);
void setLayout(std::string_view blockName, uint8_t bindingPoint, const BufferLayout& layout);
void create(std::string_view blockName);
template<typename T> // Capture value by reference, instead of decaying to pointer
void setValue(std::string_view blockName, std::string_view member, T&& value, size_t size = 0)
{
CHECK_UNIFORM_SET_CALL(blockName, member);
glBindBuffer(GL_UNIFORM_BUFFER, block.id);
glBufferSubData(GL_UNIFORM_BUFFER, block.uniformLocations.at(member.data()), (size) ? size : sizeof(T), &value);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
// Exceptions:
void setValue(std::string_view blockName, std::string_view member, bool value);
void setValue(std::string_view blockName, std::string_view member, glm::mat2 value);
void setValue(std::string_view blockName, std::string_view member, glm::mat3 value);
bool exists(std::string_view blockName) const { return m_blocks.find(blockName) != m_blocks.end(); }
private:
uint8_t m_maxBindingPoints { 0 };
std::unordered_map<std::string_view, UniformbufferBlock> m_blocks;
};
} // namespace Inferno
#if 0
// -----------------------------------------
// Example usage:
Uniformbuffer::the().setLayout(
"ExampleBlock", 0,
{
{ BufferElementType::Mat3, "a" },
{ BufferElementType::Float, "b" },
{ BufferElementType::Vec2, "c" },
{ BufferElementType::Vec2, "d" },
{ BufferElementType::Float, "e" },
});
Uniformbuffer::the().create("ExampleBlock");
#endif
// -----------------------------------------
// Memory alignment of uniform blocks using std140
//
// Main points:
// - Memory is organized into chunks.
// - A block is at least the size of 1 chunk.
// - One chunk has 4 slots, 4 bytes per slot.
// - Can't fit? Move to next chunk.
//
// The rules:
// 1. Scalar (bool, int, uint, float) takes up 1 slot, can appear after anything
// 2. Vec2 takes up 2 slots, in first or last half of a chunk
// 3. Vec3 takes up 3 slots, only at the start of a chunk
// 4. Everything else:
// - Take up maxumum room
// - As often as needed
// - Add padding as needed
// 5. Mat3 (or any matrix) are treated like arrays
// 6. Each member of *any* array gets its own chunk
// TODO: How do double types work?
// -----------------------------------------
// References:
// - https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL
// - https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout
// - https://www.oreilly.com/library/view/opengl-programming-guide/9780132748445/app09lev1sec2.html (The std140 Layout Rules)
// - https://www.youtube.com/watch?v=JPvbRko9lBg (WebGL 2: Uniform Buffer Objects)
+117 -57
View File
@@ -1,29 +1,40 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022-2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <cstdint> // uint32_t
#include <limits> // std::numeric_limits
#include <utility> // std::pair
#include "entt/entity/fwd.hpp" // ent::entity
#include "ruc/file.h"
#include "ruc/format/log.h"
#include "ruc/json/json.h"
#include "ruc/meta/assert.h"
#include "inferno/component/cameracomponent.h"
#include "inferno/component/cubemap-component.h"
#include "inferno/component/id-component.h"
#include "inferno/component/luascriptcomponent.h"
#include "inferno/component/model-component.h"
#include "inferno/component/nativescriptcomponent.h"
#include "inferno/component/spritecomponent.h"
#include "inferno/component/tagcomponent.h"
#include "inferno/component/textareacomponent.h"
#include "inferno/component/transformcomponent.h"
#include "inferno/render/renderer.h"
#include "inferno/render/uniformbuffer.h"
#include "inferno/scene/scene.h"
#include "inferno/script/cameracontroller.h"
#include "inferno/script/nativescript.h"
#include "inferno/system/camerasystem.h"
#include "inferno/system/rendersystem.h"
#include "inferno/system/scriptsystem.h"
#include "inferno/system/textareasystem.h"
#include "inferno/system/transformsystem.h"
#include "inferno/uid.h"
namespace Inferno {
@@ -45,51 +56,19 @@ void Scene::initialize()
auto sceneJson = ruc::Json::parse(ruc::File("assets/scene/scene1.json").data());
// Camera
VERIFY(sceneJson.exists("camera"), "scene doesnt contain a camera");
auto& cameraJson = sceneJson.at("camera");
uint32_t camera = loadEntity(cameraJson);
auto cameraType = CameraType::Perspective;
if (cameraJson.exists("type") && cameraJson.at("type").get<std::string>() == "orthographic") {
cameraType = CameraType::Orthographic;
}
addComponent<CameraComponent>(camera, cameraType);
if (cameraJson.exists("script")) {
auto& cameraScript = cameraJson.at("script");
if (cameraScript.exists("type") && cameraScript.exists("name")) {
if (cameraScript.at("type").get<std::string>() == "lua") {
addComponent<LuaScriptComponent>(camera, cameraScript.at("name").get<std::string>());
}
else {
addComponent<NativeScriptComponent>(camera).bind<CameraController>();
}
}
if (sceneJson.exists("init")) {
// TODO: load either NativeScript or LuaScript?
}
// Quads
// Entities
// -------------------------------------
if (sceneJson.exists("quad") && sceneJson.at("quad").type() == ruc::Json::Type::Array) {
auto& quads = sceneJson.at("quad").asArray().elements();
for (const auto& quad : quads) {
uint32_t quadEntity = loadEntity(quad);
addComponent<SpriteComponent>(quadEntity);
auto& spriteComponent = getComponent<SpriteComponent>(quadEntity);
quad.getTo(spriteComponent);
}
}
// Text
if (sceneJson.exists("text") && sceneJson.at("text").type() == ruc::Json::Type::Array) {
auto& texts = sceneJson.at("text").asArray().elements();
for (const auto& text : texts) {
uint32_t textEntity = loadEntity(text);
addComponent<TextAreaComponent>(textEntity);
auto& textAreaComponent = getComponent<TextAreaComponent>(textEntity);
text.getTo(textAreaComponent);
if (sceneJson.exists("entities")) {
const auto& entityJson = sceneJson.at("entities");
VERIFY(entityJson.type() == ruc::Json::Type::Array);
const auto& entities = entityJson.asArray();
for (size_t i = 0; i < entities.size(); ++i) {
loadEntity(entities.at(i));
}
}
@@ -107,7 +86,6 @@ void Scene::update(float deltaTime)
void Scene::render()
{
RenderSystem::the().render();
TextAreaSystem::the().render();
}
void Scene::destroy()
@@ -118,40 +96,122 @@ void Scene::destroy()
TransformSystem::destroy();
}
// -----------------------------------------
uint32_t Scene::createEntity(const std::string& name)
{
return createEntityWithUID(UID(), name);
}
uint32_t Scene::createEntityWithUID(UID id, const std::string& name)
{
uint32_t entity = static_cast<uint32_t>(m_registry->create());
addComponent<IDComponent>(entity, id);
addComponent<TagComponent>(entity, name.empty() ? "Unnamed Entity" : name);
addComponent<TransformComponent>(entity);
TransformSystem::the().add(entity);
return entity;
}
uint32_t Scene::loadEntity(ruc::Json components, uint32_t parentEntity)
{
VERIFY(components.type() == ruc::Json::Type::Object);
uint32_t entity = createEntity();
// At minimum, ID is required
VERIFY(components.exists("id"), "id not found");
auto& id = getComponent<IDComponent>(entity);
components.at("id").getTo(id);
if (components.exists("tag")) {
auto& tag = getComponent<TagComponent>(entity);
components.at("tag").getTo(tag);
}
if (components.exists("transform")) {
auto& transform = getComponent<TransformComponent>(entity);
components.at("transform").getTo(transform);
transform.parent = static_cast<entt::entity>(parentEntity);
}
if (components.exists("camera")) {
auto& camera = addComponent<CameraComponent>(entity);
components.at("camera").getTo(camera);
}
if (components.exists("lua-scripts")) {
VERIFY(components.at("lua-scripts").type() == ruc::Json::Type::Array);
const auto& scripts = components.at("lua-scripts").asArray();
for (size_t i = 0; i < scripts.size(); ++i) {
auto& script = addComponent<LuaScriptComponent>(entity);
scripts.at(i).getTo(script);
}
}
if (components.exists("native-scripts")) {
VERIFY(components.at("native-scripts").type() == ruc::Json::Type::Array);
const auto& scripts = components.at("native-scripts").asArray();
for (size_t i = 0; i < scripts.size(); ++i) {
auto& script = addComponent<NativeScriptComponent>(entity);
scripts.at(i).getTo(script);
script.bind();
}
}
if (components.exists("sprite")) {
auto& sprite = addComponent<SpriteComponent>(entity);
components.at("sprite").getTo(sprite);
}
if (components.exists("cubemap")) {
auto& cubemap = addComponent<CubemapComponent>(entity);
components.at("cubemap").getTo(cubemap);
}
if (components.exists("text")) {
auto& text = addComponent<TextAreaComponent>(entity);
components.at("text").getTo(text);
}
if (components.exists("model")) {
auto& text = addComponent<ModelComponent>(entity);
components.at("model").getTo(text);
}
if (components.exists("children")) {
VERIFY(components.at("children").type() == ruc::Json::Type::Array);
const auto& children = components.at("children").asArray();
for (size_t i = 0; i < children.size(); ++i) {
loadEntity(components.at("children")[i], entity);
}
}
return entity;
}
uint32_t Scene::findEntity(std::string_view name)
{
auto view = m_registry->view<TagComponent>();
for (auto [entity, tag] : view.each()) {
if (tag.tag == name) {
return static_cast<uint32_t>(entity);
}
}
return std::numeric_limits<uint32_t>::max();
}
void Scene::destroyEntity(uint32_t entity)
{
ScriptSystem::the().cleanup(entity);
m_registry->destroy(entt::entity { entity });
}
uint32_t Scene::loadEntity(ruc::Json json)
{
uint32_t entity = createEntity((json.exists("name"))
? json.at("name").get<std::string>()
: "");
auto& transform = getComponent<TransformComponent>(entity);
json.getTo(transform);
// -----------------------------------------
return entity;
}
glm::mat4 Scene::cameraProjectionView()
std::pair<glm::mat4, glm::mat4> Scene::cameraProjectionView()
{
return CameraSystem::the().projectionView();
}
void Scene::validEntity(uint32_t entity) const
{
VERIFY(m_registry->valid(entt::entity { entity }), "Entity is not valid");
VERIFY(m_registry->valid(entt::entity { entity }), "invalid entity '{}'", entity);
}
} // namespace Inferno
+15 -6
View File
@@ -1,19 +1,23 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstddef> // size_t
#include <cstdint> // uint32_t
#include <memory> // std::shared_ptr
#include <utility> // std::pair
#include "entt/entity/registry.hpp" // entt::entity, entt::registry
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
#include "ruc/format/format.h"
#include "ruc/json/json.h"
#include "inferno/uid.h"
namespace Inferno {
class Camera;
@@ -27,11 +31,15 @@ public:
void destroy();
uint32_t createEntity(const std::string& name = "");
uint32_t createEntityWithUID(UID id, const std::string& name = "");
uint32_t loadEntity(ruc::Json components, uint32_t parentEntity = entt::null);
uint32_t findEntity(std::string_view name);
void destroyEntity(uint32_t entity);
uint32_t loadEntity(ruc::Json json);
glm::mat4 cameraProjectionView();
/**
* @brief Return a pair from the camera component: { projection, view }
*/
std::pair<glm::mat4, glm::mat4> cameraProjectionView();
void validEntity(uint32_t entity) const;
@@ -49,7 +57,8 @@ public:
return m_registry->any<T...>(entt::entity { entity });
}
// @Todo Should replace be allowed? could trigger memory leaks with nativescript
// TODO: Should replace be allowed? could trigger memory leaks with nativescript
// TODO: Replace will make it so an entity cant have multiple scripts
template<typename T, typename... P>
T& addComponent(uint32_t entity, P&&... parameters) const
{
@@ -64,7 +73,7 @@ public:
return m_registry->remove_if_exists<T>(entt::entity { entity });
}
// @Todo Should replace be allowed? could trigger memory leaks with nativescript
// TODO: Should replace be allowed? could trigger memory leaks with nativescript
template<typename T, typename... P>
T& getComponent(uint32_t entity, P&&... parameters) const
{
+50
View File
@@ -6,6 +6,9 @@
#pragma once
#include "ruc/meta/core.h"
#include "ruc/singleton.h"
#include "inferno/scene/scene.h"
namespace Inferno {
@@ -14,6 +17,9 @@ struct TransformComponent;
class NativeScript {
public:
typedef NativeScript* (*InitializeFunction)();
typedef void (*DestroyFunction)(NativeScript*);
virtual ~NativeScript() {}
protected:
@@ -36,4 +42,48 @@ private:
friend class ScriptSystem;
};
class NativeScriptBinding final : public ruc::Singleton<NativeScriptBinding> {
public:
NativeScriptBinding(s) {}
virtual ~NativeScriptBinding() {}
void registerBinding(const std::string& binding,
NativeScript::InitializeFunction initialize,
NativeScript::DestroyFunction destroy)
{
m_initializeBindings.emplace(binding, initialize);
m_detroyBindings.emplace(binding, destroy);
}
NativeScript::InitializeFunction initializeBinding(const std::string& name) { return m_initializeBindings[name]; }
NativeScript::DestroyFunction destroyBinding(const std::string& name) { return m_detroyBindings[name]; }
private:
std::unordered_map<std::string, NativeScript::InitializeFunction> m_initializeBindings;
std::unordered_map<std::string, NativeScript::DestroyFunction> m_detroyBindings;
};
} // namespace Inferno
// TODO: Move this to ruc
#define CONCAT(a, b) CONCAT_IMPL(a, b)
#define CONCAT_IMPL(a, b) a##b
#define BIND_NATIVE_IMPL(name, struct_name) \
struct struct_name { \
struct_name() \
{ \
Inferno::NativeScriptBinding::the().registerBinding( \
#name, \
[]() -> Inferno::NativeScript* { return static_cast<Inferno::NativeScript*>(new name()); }, \
[](Inferno::NativeScript* nativeScript) -> void { \
VERIFY(nativeScript, "Attempting to destroy an uninitialized NativeScript"); \
delete static_cast<name*>(nativeScript); \
nativeScript = nullptr; \
}); \
} \
}; \
static struct struct_name struct_name; // NOLINT(clang-diagnostic-unused-function)
#define BIND_NATIVE(name) \
BIND_NATIVE_IMPL(name, CONCAT(__BIND_NATIVE_FUNCTION_, __COUNTER__))
+20 -5
View File
@@ -1,9 +1,11 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <utility> // std::pair
#include "glm/ext/matrix_clip_space.hpp" // glm::perspective, glm::ortho
#include "glm/ext/matrix_transform.hpp" // glm::radians, glm::lookAt
#include "ruc/format/log.h"
@@ -42,19 +44,32 @@ void CameraSystem::update()
}
}
glm::mat4 CameraSystem::projectionView()
std::pair<glm::mat4, glm::mat4> CameraSystem::projectionView()
{
auto view = m_registry->view<TransformComponent, CameraComponent>();
for (auto [entity, transform, camera] : view.each()) {
return camera.projection * transform.transform;
return { camera.projection, transform.transform };
}
VERIFY_NOT_REACHED();
return glm::mat4 { 1.0f };
return {};
}
glm::vec3 CameraSystem::translate()
{
auto view = m_registry->view<TransformComponent, CameraComponent>();
for (auto [entity, transform, camera] : view.each()) {
return transform.translate;
}
VERIFY_NOT_REACHED();
return {};
}
// -----------------------------------------
void CameraSystem::updateOrthographic(TransformComponent& transform, CameraComponent& camera)
{
// Update camera matrix

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