Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc33b5d481 | ||
|
|
67bb6a5671 | ||
|
|
3ff8838e01 | ||
|
|
3ae6ecae8a | ||
|
|
6b6c588378 | ||
|
|
9145f90c1d | ||
|
|
b9043dbb4b | ||
|
|
b6e68eccec | ||
|
|
d16fade497 | ||
|
|
b8e4d9ef3f | ||
|
|
03e8210165 | ||
|
|
dcf2a85208 | ||
|
|
b2ed951b1e | ||
|
|
d63dfe2f9f | ||
|
|
faacdca424 | ||
|
|
b5f3cae5ba | ||
|
|
1d5f5a1ad8 | ||
|
|
3cd2fab637 | ||
|
|
c5ed219ad2 | ||
|
|
4dbac7ee38 | ||
|
|
a5b7a49447 | ||
|
|
fd72f6610e | ||
|
|
21319b93ad | ||
|
|
cd36841039 | ||
|
|
538d0b5ce7 | ||
|
|
5f6a5f48dd | ||
|
|
c06b06ed62 | ||
|
|
5721b4249c | ||
|
|
95a64be4cd | ||
|
|
79b4f1c3d1 | ||
|
|
a266a27f88 | ||
|
|
08969e32d1 | ||
|
|
2793956be4 | ||
|
|
51c5874318 | ||
|
|
d01a4429ca | ||
|
|
d22effdfa3 | ||
|
|
52611beeab | ||
|
|
fd8973d10d | ||
|
|
bc3f2c9db5 | ||
|
|
0dd7a05d46 | ||
|
|
c607ebcc72 | ||
|
|
15b71f8387 | ||
|
|
6e5f82913e | ||
|
|
d02d5a528b | ||
|
|
574f2a8b5a | ||
|
|
2b0635ed69 | ||
|
|
649d196577 | ||
|
|
f47babe5cf | ||
|
|
efae5b9adb | ||
|
|
a1b02c2e3a | ||
|
|
e926d3ce4a | ||
|
|
4c479cfb64 |
@@ -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
|
||||
|
||||
@@ -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,10 +81,7 @@ add_subdirectory("src")
|
||||
# ------------------------------------------
|
||||
# Assets target
|
||||
|
||||
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)
|
||||
add_subdirectory("assets")
|
||||
|
||||
# ------------------------------------------
|
||||
# Examples target
|
||||
|
||||
@@ -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]]
|
||||
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
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;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,40 +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"
|
||||
}
|
||||
},
|
||||
"quad": [
|
||||
"entities": [
|
||||
{
|
||||
"name": "Quad",
|
||||
"translate": [ 0.0, 0.0, 0.0 ],
|
||||
"rotate": [ 0.0, 0.0, 0.0 ],
|
||||
"scale": [ 1.0, 1.0, 1.0 ],
|
||||
"color": [ 1.0, 1.0, 1.0, 1.0 ],
|
||||
"texture": "assets/gfx/test.png"
|
||||
"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" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Quad 2",
|
||||
"translate": [ 1.1, 0.0, 0.0 ],
|
||||
"rotate": [ 0.0, 0.0, 0.0 ],
|
||||
"scale": [ 1.0, 1.0, 1.0 ],
|
||||
"color": [ 0.5, 0.6, 0.8, 1.0 ],
|
||||
"texture": "assets/gfx/test.png"
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Quad 3",
|
||||
"translate": [ 2.2, 0.0, 0.0 ],
|
||||
"rotate": [ 0.0, 0.0, 0.0 ],
|
||||
"scale": [ 1.0, 1.0, 1.0 ],
|
||||
"color": [ 1.0, 1.0, 1.0, 1.0 ],
|
||||
"texture": "assets/gfx/test-inverted.png"
|
||||
"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 ]
|
||||
},
|
||||
"model": {
|
||||
"color": [ 1.0, 1.0, 1.0, 1.0 ],
|
||||
"model": "assets/model/quad.obj",
|
||||
"texture": "assets/gfx/test.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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 ]
|
||||
},
|
||||
"model": {
|
||||
"color": [ 0.5, 0.6, 0.8, 1.0 ],
|
||||
"model": "assets/model/quad.obj",
|
||||
"texture": "assets/gfx/test.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": { "id": 675754 },
|
||||
"tag": { "tag": "Text" },
|
||||
"text": {
|
||||
"content": "Hello World!",
|
||||
"font": "assets/fnt/open-sans",
|
||||
"font-size": 24,
|
||||
"line-spacing": 1.0,
|
||||
"width": 150
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"fullscreen": "windowed",
|
||||
"height": 720,
|
||||
"title": "Inferno",
|
||||
"vsync": true,
|
||||
"vsync": false,
|
||||
"width": 1280
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#+TITLE: Documentation
|
||||
#+AUTHOR: Riyyi
|
||||
#+LANGUAGE: en
|
||||
#+OPTIONS: toc:nil
|
||||
|
||||
Topics:
|
||||
|
||||
- [[./shaders.org][Shaders]]
|
||||
- [[./references.org][References]]
|
||||
@@ -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]]
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#+TITLE: Shaders
|
||||
#+AUTHOR: Riyyi
|
||||
#+LANGUAGE: en
|
||||
#+OPTIONS: toc:nil
|
||||
|
||||
Topics:
|
||||
|
||||
- [[./memory-layout.org][Memory Layout]]
|
||||
@@ -15,6 +15,7 @@ add_executable(${GAME} ${GAME_SOURCES})
|
||||
target_include_directories(${GAME} PRIVATE
|
||||
"src")
|
||||
target_link_libraries(${GAME} ${ENGINE})
|
||||
target_compile_options(${GAME} PRIVATE ${COMPILE_FLAGS_PROJECT})
|
||||
|
||||
target_precompile_headers(${GAME} REUSE_FROM ${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
|
||||
@@ -7,10 +7,10 @@ target_include_directories(${ENGINE} PUBLIC
|
||||
"../vendor/glfw/include"
|
||||
"../vendor/glm"
|
||||
"../vendor/json/include"
|
||||
"../vendor/ruc/src"
|
||||
"../vendor/sol2/include"
|
||||
"../vendor/stb")
|
||||
target_link_libraries(${ENGINE} ${ENGINE}-dependencies)
|
||||
target_compile_options(${ENGINE} PRIVATE ${COMPILE_FLAGS_PROJECT})
|
||||
|
||||
# ------------------------------------------
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 });
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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 ");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
glVertexAttribPointer(
|
||||
index,
|
||||
element.getTypeCount(),
|
||||
element.getTypeGL(),
|
||||
element.getNormalized() ? GL_TRUE : GL_FALSE,
|
||||
layout.getStride(),
|
||||
reinterpret_cast<const void*>(element.getOffset()));
|
||||
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.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
|
||||
|
||||
@@ -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
|
||||
Mat2, Mat3, Mat4, // mat
|
||||
DoubleMat2, DoubleMat3, DoubleMat4, // dmat
|
||||
Bool, Bool2, Bool3, Bool4, // bvec
|
||||
Int, Int2, Int3, Int4, // ivec
|
||||
Uint, Uint2, Uint3, Uint4, // uvec
|
||||
Float, Vec2, Vec3, Vec4, // vec
|
||||
Double, Vec2Double, Vec3Double, Vec4Double, // dvec
|
||||
Mat2, Mat3, Mat4, // mat
|
||||
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 };
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <climits> // UINT_MAX
|
||||
#include <cstdint> // uint8_t, uint32_t
|
||||
#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))
|
||||
{
|
||||
unsigned char* data = nullptr;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int channels = 0;
|
||||
|
||||
// Load image data
|
||||
stbi_set_flip_vertically_on_load(1);
|
||||
data = stbi_load(path.c_str(), &width, &height, &channels, STBI_default);
|
||||
VERIFY(data, "failed to load image: '{}'", path);
|
||||
|
||||
init(data, width, height, channels);
|
||||
|
||||
// Clean resources
|
||||
stbi_image_free(data);
|
||||
}
|
||||
|
||||
Texture::Texture(unsigned char* data, uint32_t width, uint32_t height, uint8_t channels)
|
||||
{
|
||||
init(data, width, height, channels);
|
||||
}
|
||||
|
||||
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::init(unsigned char* data, uint32_t width, uint32_t height, uint8_t channels)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // uint8_t, 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);
|
||||
Texture(unsigned char* data, uint32_t width, uint32_t height, uint8_t channels = 3);
|
||||
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 init(unsigned char* data, uint32_t width, uint32_t height, uint8_t channels);
|
||||
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);
|
||||
~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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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__))
|
||||
|
||||
@@ -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
|
||||
|
||||