Compare commits
37
Commits
fd8973d10d
...
master
| 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 |
@@ -19,3 +19,6 @@
|
|||||||
[submodule "inferno/vendor/ruc"]
|
[submodule "inferno/vendor/ruc"]
|
||||||
path = vendor/ruc
|
path = vendor/ruc
|
||||||
url = https://github.com/Riyyi/ruc
|
url = https://github.com/Riyyi/ruc
|
||||||
|
[submodule "inferno/vendor/assimp"]
|
||||||
|
path = vendor/assimp
|
||||||
|
url = https://github.com/assimp/assimp
|
||||||
|
|||||||
+19
-7
@@ -11,6 +11,7 @@ endif()
|
|||||||
# Options
|
# Options
|
||||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||||
option(INFERNO_BUILD_EXAMPLES "Build the Inferno example programs" ${INFERNO_STANDALONE})
|
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)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
# Compiler flags used for all build types
|
# Compiler flags used for all build types
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
|
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
|
# -Wall = All warnings about contructions that are easily avoidable
|
||||||
# -Wextra = Extra warning flags not covered by -Wall
|
# -Wextra = Extra warning flags not covered by -Wall
|
||||||
# -Wpedantic = Warnings for compiler extensions not part of the standard
|
# -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 if not specified
|
||||||
set(DEFAULT_BUILD_TYPE Release)
|
set(DEFAULT_BUILD_TYPE Release)
|
||||||
@@ -42,14 +48,20 @@ endif()
|
|||||||
# Set build type specific compiler flags
|
# Set build type specific compiler flags
|
||||||
message("--- ${CMAKE_BUILD_TYPE} ---")
|
message("--- ${CMAKE_BUILD_TYPE} ---")
|
||||||
if(${CMAKE_BUILD_TYPE} STREQUAL Debug)
|
if(${CMAKE_BUILD_TYPE} STREQUAL Debug)
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -g -pg")
|
# Optimizations that do not interfere with debugging
|
||||||
# -Og = Optimizations that do not interfere with debugging
|
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -Og")
|
||||||
# -g = Produce debugging information in OS's native format
|
# Produce debugging information in OS's native format
|
||||||
# -pg = Generate profile information for analysis with gprof
|
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -g")
|
||||||
|
# Generate profile information for analysis with gprof
|
||||||
# $ gprof <PROJECT> gmon.out > profile-data.txt
|
# $ 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)
|
elseif(${CMAKE_BUILD_TYPE} STREQUAL Release)
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
# Optimizations that increase compilation time and performance
|
||||||
# -O3 = Optimizations that increase compilation time and performance
|
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -O3")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ $ cmake .. && make
|
|||||||
|
|
||||||
* Libraries
|
* Libraries
|
||||||
|
|
||||||
|
- [[https://github.com/assimp/assimp][assimp]]
|
||||||
# - [[https://github.com/bulletphysics/bullet3][Bullet]]
|
# - [[https://github.com/bulletphysics/bullet3][Bullet]]
|
||||||
- [[https://github.com/skypjack/entt][EnTT]]
|
- [[https://github.com/skypjack/entt][EnTT]]
|
||||||
- [[https://github.com/Dav1dde/glad][glad]]
|
- [[https://github.com/Dav1dde/glad][glad]]
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ layout(location = 0) out vec4 color;
|
|||||||
|
|
||||||
in vec4 v_color;
|
in vec4 v_color;
|
||||||
in vec2 v_textureCoordinates;
|
in vec2 v_textureCoordinates;
|
||||||
in flat float v_textureIndex;
|
in flat uint v_textureIndex;
|
||||||
|
|
||||||
uniform sampler2D u_textures[32];
|
uniform sampler2D u_textures[32];
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec4 textureColor = v_color;
|
vec4 textureColor = v_color;
|
||||||
switch(int(v_textureIndex)) {
|
switch(v_textureIndex) {
|
||||||
case 0: break; // Texture unit 0 is reserved for no texture
|
case 0: break; // Texture unit 0 is reserved for no texture
|
||||||
case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
|
case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
|
||||||
case 2: textureColor *= texture(u_textures[2], 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);
|
||||||
|
}
|
||||||
@@ -4,14 +4,14 @@ layout(location = 0) out vec4 color;
|
|||||||
|
|
||||||
in vec4 v_color;
|
in vec4 v_color;
|
||||||
in vec3 v_textureCoordinates;
|
in vec3 v_textureCoordinates;
|
||||||
in flat float v_textureIndex;
|
in flat uint v_textureIndex;
|
||||||
|
|
||||||
uniform samplerCube u_textures[32];
|
uniform samplerCube u_textures[32];
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec4 textureColor = v_color;
|
vec4 textureColor = v_color;
|
||||||
switch(int(v_textureIndex)) {
|
switch(v_textureIndex) {
|
||||||
case 0: break; // Texture unit 0 is reserved for no texture
|
case 0: break; // Texture unit 0 is reserved for no texture
|
||||||
case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
|
case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
|
||||||
case 2: textureColor *= texture(u_textures[2], v_textureCoordinates); break;
|
case 2: textureColor *= texture(u_textures[2], v_textureCoordinates); break;
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
layout(location = 0) in vec3 a_position;
|
layout(location = 0) in vec3 a_position;
|
||||||
layout(location = 1) in vec4 a_color;
|
layout(location = 1) in vec4 a_color;
|
||||||
layout(location = 2) in float a_textureIndex;
|
layout(location = 2) in uint a_textureIndex;
|
||||||
|
|
||||||
out vec4 v_color;
|
out vec4 v_color;
|
||||||
out vec3 v_textureCoordinates;
|
out vec3 v_textureCoordinates;
|
||||||
out flat float v_textureIndex;
|
out flat uint v_textureIndex;
|
||||||
|
|
||||||
uniform mat4 u_projectionView;
|
uniform mat4 u_projectionView;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ layout(location = 0) out vec4 color;
|
|||||||
|
|
||||||
in vec4 v_color;
|
in vec4 v_color;
|
||||||
in vec2 v_textureCoordinates;
|
in vec2 v_textureCoordinates;
|
||||||
in flat float v_textureIndex;
|
in flat uint v_textureIndex;
|
||||||
in float v_width;
|
in float v_width;
|
||||||
in float v_edge;
|
in float v_edge;
|
||||||
in float v_borderWidth;
|
in float v_borderWidth;
|
||||||
@@ -25,7 +25,7 @@ float alpha(float textureAlpha)
|
|||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec4 textureColor = v_color;
|
vec4 textureColor = v_color;
|
||||||
switch(int(v_textureIndex)) {
|
switch(v_textureIndex) {
|
||||||
case 0: break; // Texture unit 0 is reserved for no texture
|
case 0: break; // Texture unit 0 is reserved for no texture
|
||||||
// case 1: textureColor.a = 1; break;
|
// case 1: textureColor.a = 1; break;
|
||||||
case 1: textureColor.a = alpha(texture(u_textures[1], v_textureCoordinates).a); 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 = 0) in vec3 a_position;
|
||||||
layout(location = 1) in vec4 a_color;
|
layout(location = 1) in vec4 a_color;
|
||||||
layout(location = 2) in vec2 a_textureCoordinates;
|
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 = 4) in float a_width;
|
||||||
layout(location = 5) in float a_edge;
|
layout(location = 5) in float a_edge;
|
||||||
layout(location = 6) in float a_borderWidth;
|
layout(location = 6) in float a_borderWidth;
|
||||||
@@ -13,7 +13,7 @@ layout(location = 9) in float a_offset;
|
|||||||
|
|
||||||
out vec4 v_color;
|
out vec4 v_color;
|
||||||
out vec2 v_textureCoordinates;
|
out vec2 v_textureCoordinates;
|
||||||
out flat float v_textureIndex;
|
out flat uint v_textureIndex;
|
||||||
out float v_width;
|
out float v_width;
|
||||||
out float v_edge;
|
out float v_edge;
|
||||||
out float v_borderWidth;
|
out float v_borderWidth;
|
||||||
@@ -33,7 +33,4 @@ void main()
|
|||||||
v_borderColor = a_borderColor;
|
v_borderColor = a_borderColor;
|
||||||
v_offset = a_offset;
|
v_offset = a_offset;
|
||||||
gl_Position = vec4(a_position, 1.0f);
|
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;
|
||||||
|
}
|
||||||
@@ -2,19 +2,22 @@
|
|||||||
|
|
||||||
layout(location = 0) in vec3 a_position;
|
layout(location = 0) in vec3 a_position;
|
||||||
layout(location = 1) in vec4 a_color;
|
layout(location = 1) in vec4 a_color;
|
||||||
layout(location = 2) in vec2 a_textureCoordinates;
|
layout(location = 2) in uint a_textureIndex;
|
||||||
layout(location = 3) in float a_textureIndex;
|
|
||||||
|
|
||||||
out vec4 v_color;
|
out vec4 v_color;
|
||||||
out vec2 v_textureCoordinates;
|
out vec3 v_textureCoordinates;
|
||||||
out flat float v_textureIndex;
|
out flat uint v_textureIndex;
|
||||||
|
|
||||||
uniform mat4 u_projectionView;
|
layout(std140, binding = 0) uniform Camera
|
||||||
|
{
|
||||||
|
mat4 u_projectionView;
|
||||||
|
vec3 u_position;
|
||||||
|
};
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
v_color = a_color;
|
v_color = a_color;
|
||||||
v_textureCoordinates = a_textureCoordinates;
|
v_textureCoordinates = a_position;
|
||||||
v_textureIndex = a_textureIndex;
|
v_textureIndex = a_textureIndex;
|
||||||
// Vclip = Camera projection * Camera view * Model transform * Vlocal
|
// Vclip = Camera projection * Camera view * Model transform * Vlocal
|
||||||
gl_Position = u_projectionView * vec4(a_position, 1.0f);
|
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);
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -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
|
||||||
@@ -35,8 +35,9 @@
|
|||||||
"rotate": [ 0.0, 0.0, 0.0 ],
|
"rotate": [ 0.0, 0.0, 0.0 ],
|
||||||
"scale": [ 1.0, 1.0, 1.0 ]
|
"scale": [ 1.0, 1.0, 1.0 ]
|
||||||
},
|
},
|
||||||
"sprite": {
|
"model": {
|
||||||
"color": [ 1.0, 1.0, 1.0, 1.0 ],
|
"color": [ 1.0, 1.0, 1.0, 1.0 ],
|
||||||
|
"model": "assets/model/quad.obj",
|
||||||
"texture": "assets/gfx/test.png"
|
"texture": "assets/gfx/test.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -44,12 +45,13 @@
|
|||||||
"id": { "id": 97897897 },
|
"id": { "id": 97897897 },
|
||||||
"tag": { "tag": "Quad 2" },
|
"tag": { "tag": "Quad 2" },
|
||||||
"transform" : {
|
"transform" : {
|
||||||
"translate": [ 1.1, 0.0, 0.0 ],
|
"translate": [ 2.5, 0.0, 0.0 ],
|
||||||
"rotate": [ 0.0, 0.0, 0.0 ],
|
"rotate": [ 0.0, 0.0, 0.0 ],
|
||||||
"scale": [ 1.0, 1.0, 1.0 ]
|
"scale": [ 1.0, 1.0, 1.0 ]
|
||||||
},
|
},
|
||||||
"sprite": {
|
"model": {
|
||||||
"color": [ 0.5, 0.6, 0.8, 1.0 ],
|
"color": [ 0.5, 0.6, 0.8, 1.0 ],
|
||||||
|
"model": "assets/model/quad.obj",
|
||||||
"texture": "assets/gfx/test.png"
|
"texture": "assets/gfx/test.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -57,12 +59,13 @@
|
|||||||
"id": { "id": 3424242 },
|
"id": { "id": 3424242 },
|
||||||
"tag": { "tag": "Quad 3" },
|
"tag": { "tag": "Quad 3" },
|
||||||
"transform" : {
|
"transform" : {
|
||||||
"translate": [ 2.2, 1.0, 0.0 ],
|
"translate": [ 5.0, 1.0, 0.0 ],
|
||||||
"rotate": [ 0.0, 0.0, -20.0 ],
|
"rotate": [ 0.0, 0.0, -20.0 ],
|
||||||
"scale": [ 1.0, 1.0, 1.0 ]
|
"scale": [ 1.0, 1.0, 1.0 ]
|
||||||
},
|
},
|
||||||
"sprite": {
|
"model": {
|
||||||
"color": [ 1.0, 1.0, 1.0, 1.0 ],
|
"color": [ 1.0, 1.0, 1.0, 1.0 ],
|
||||||
|
"model": "assets/model/quad.obj",
|
||||||
"texture": "assets/gfx/test-inverted.png"
|
"texture": "assets/gfx/test-inverted.png"
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
@@ -70,12 +73,13 @@
|
|||||||
"id": { "id": 4345472 },
|
"id": { "id": 4345472 },
|
||||||
"tag": { "tag": "Quad 4" },
|
"tag": { "tag": "Quad 4" },
|
||||||
"transform" : {
|
"transform" : {
|
||||||
"translate": [ 0.85, 0.0, 0.0 ],
|
"translate": [ 1.7, 0.0, 0.0 ],
|
||||||
"rotate": [ 0.0, 0.0, 0.0 ],
|
"rotate": [ 0.0, 0.0, 0.0 ],
|
||||||
"scale": [ 0.5, 0.5, 1.0 ]
|
"scale": [ 0.5, 0.5, 1.0 ]
|
||||||
},
|
},
|
||||||
"sprite": {
|
"model": {
|
||||||
"color": [ 1.0, 1.0, 1.0, 1.0 ],
|
"color": [ 1.0, 1.0, 1.0, 1.0 ],
|
||||||
|
"model": "assets/model/quad.obj",
|
||||||
"texture": "assets/gfx/test-inverted.png"
|
"texture": "assets/gfx/test-inverted.png"
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
@@ -83,12 +87,13 @@
|
|||||||
"id": { "id": 5234723 },
|
"id": { "id": 5234723 },
|
||||||
"tag": { "tag": "Quad 5" },
|
"tag": { "tag": "Quad 5" },
|
||||||
"transform" : {
|
"transform" : {
|
||||||
"translate": [ 1.0, 0.0, 0.0 ],
|
"translate": [ 2.0, 0.0, 0.0 ],
|
||||||
"rotate": [ 0.0, 0.0, -20.0 ],
|
"rotate": [ 0.0, 0.0, -20.0 ],
|
||||||
"scale": [ 0.5, 0.5, 1.0 ]
|
"scale": [ 0.5, 0.5, 1.0 ]
|
||||||
},
|
},
|
||||||
"sprite": {
|
"model": {
|
||||||
"color": [ 1.0, 1.0, 1.0, 1.0 ],
|
"color": [ 1.0, 1.0, 1.0, 1.0 ],
|
||||||
|
"model": "assets/model/quad.obj",
|
||||||
"texture": "assets/gfx/test-inverted.png"
|
"texture": "assets/gfx/test-inverted.png"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"fullscreen": "windowed",
|
"fullscreen": "windowed",
|
||||||
"height": 720,
|
"height": 720,
|
||||||
"title": "Inferno",
|
"title": "Inferno",
|
||||||
"vsync": true,
|
"vsync": false,
|
||||||
"width": 1280
|
"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
|
target_include_directories(${GAME} PRIVATE
|
||||||
"src")
|
"src")
|
||||||
target_link_libraries(${GAME} ${ENGINE})
|
target_link_libraries(${GAME} ${ENGINE})
|
||||||
|
target_compile_options(${GAME} PRIVATE ${COMPILE_FLAGS_PROJECT})
|
||||||
|
|
||||||
target_precompile_headers(${GAME} REUSE_FROM ${ENGINE})
|
target_precompile_headers(${GAME} REUSE_FROM ${ENGINE})
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ target_include_directories(${ENGINE} PUBLIC
|
|||||||
"../vendor/sol2/include"
|
"../vendor/sol2/include"
|
||||||
"../vendor/stb")
|
"../vendor/stb")
|
||||||
target_link_libraries(${ENGINE} ${ENGINE}-dependencies)
|
target_link_libraries(${ENGINE} ${ENGINE}-dependencies)
|
||||||
|
target_compile_options(${ENGINE} PRIVATE ${COMPILE_FLAGS_PROJECT})
|
||||||
|
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
|
|
||||||
|
|||||||
+32
-27
@@ -6,26 +6,30 @@
|
|||||||
|
|
||||||
#include <utility> // std::pair
|
#include <utility> // std::pair
|
||||||
|
|
||||||
|
#include "glm/ext/vector_float3.hpp"
|
||||||
#include "glm/gtc/type_ptr.hpp" // glm::make_mat4
|
#include "glm/gtc/type_ptr.hpp" // glm::make_mat4
|
||||||
#include "ruc/format/log.h"
|
#include "ruc/format/log.h"
|
||||||
#include "ruc/meta/assert.h"
|
#include "ruc/meta/assert.h"
|
||||||
|
|
||||||
#include "inferno/application.h"
|
#include "inferno/application.h"
|
||||||
|
#include "inferno/component/transformcomponent.h"
|
||||||
#include "inferno/core.h"
|
#include "inferno/core.h"
|
||||||
#include "inferno/event/applicationevent.h"
|
#include "inferno/event/applicationevent.h"
|
||||||
#include "inferno/event/event.h"
|
#include "inferno/event/event.h"
|
||||||
#include "inferno/event/keyevent.h"
|
#include "inferno/event/keyevent.h"
|
||||||
#include "inferno/event/mouseevent.h"
|
#include "inferno/event/mouseevent.h"
|
||||||
// #include "inferno/io/gltffile.h"
|
|
||||||
#include "inferno/io/input.h"
|
#include "inferno/io/input.h"
|
||||||
#include "inferno/keycodes.h"
|
#include "inferno/keycodes.h"
|
||||||
#include "inferno/render/buffer.h"
|
#include "inferno/render/buffer.h"
|
||||||
#include "inferno/render/context.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/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/renderer.h"
|
||||||
#include "inferno/render/shader.h"
|
|
||||||
#include "inferno/render/texture.h"
|
|
||||||
#include "inferno/scene/scene.h"
|
#include "inferno/scene/scene.h"
|
||||||
#include "inferno/settings.h"
|
#include "inferno/settings.h"
|
||||||
#include "inferno/time.h"
|
#include "inferno/time.h"
|
||||||
@@ -50,12 +54,11 @@ Application::Application()
|
|||||||
|
|
||||||
Input::initialize();
|
Input::initialize();
|
||||||
RenderCommand::initialize();
|
RenderCommand::initialize();
|
||||||
|
RenderSystem::the().initialize(m_window->getWidth(), m_window->getHeight());
|
||||||
|
|
||||||
m_scene = std::make_shared<Scene>();
|
m_scene = std::make_shared<Scene>();
|
||||||
m_scene->initialize();
|
m_scene->initialize();
|
||||||
|
|
||||||
// Load assets
|
|
||||||
|
|
||||||
// m_font = FontManager::the().load("assets/fnt/dejavu-sans");
|
// m_font = FontManager::the().load("assets/fnt/dejavu-sans");
|
||||||
|
|
||||||
// auto bla = GlTFFile::read("assets/gltf/box.glb");
|
// auto bla = GlTFFile::read("assets/gltf/box.glb");
|
||||||
@@ -76,14 +79,16 @@ Application::Application()
|
|||||||
Application::~Application()
|
Application::~Application()
|
||||||
{
|
{
|
||||||
m_scene->destroy();
|
m_scene->destroy();
|
||||||
|
Uniformbuffer::destroy();
|
||||||
|
|
||||||
FontManager::destroy();
|
RendererFont::destroy();
|
||||||
RendererCharacter::destroy();
|
|
||||||
Renderer2D::destroy();
|
Renderer2D::destroy();
|
||||||
|
Renderer3D::destroy();
|
||||||
RendererCubemap::destroy();
|
RendererCubemap::destroy();
|
||||||
|
RendererPostProcess::destroy();
|
||||||
|
RendererLightCube::destroy();
|
||||||
RenderCommand::destroy();
|
RenderCommand::destroy();
|
||||||
TextureManager::destroy();
|
AssetManager::destroy();
|
||||||
ShaderManager::destroy();
|
|
||||||
// Input::destroy();
|
// Input::destroy();
|
||||||
|
|
||||||
Settings::destroy();
|
Settings::destroy();
|
||||||
@@ -145,13 +150,20 @@ int Application::run()
|
|||||||
// offset
|
// offset
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
double gametime = 0;
|
||||||
|
uint64_t frames = 0;
|
||||||
|
|
||||||
while (!m_window->shouldClose()) {
|
while (!m_window->shouldClose()) {
|
||||||
|
|
||||||
float time = Time::time();
|
float time = Time::time();
|
||||||
float deltaTime = time - m_lastFrameTime;
|
float deltaTime = time - m_lastFrameTime;
|
||||||
m_lastFrameTime = time;
|
m_lastFrameTime = time;
|
||||||
// ruc::debug("Frametime " << deltaTime * 1000 << "ms");
|
// ruc::debug("Frametime {}ms", deltaTime * 1000);
|
||||||
|
|
||||||
|
gametime += deltaTime;
|
||||||
|
frames++;
|
||||||
|
|
||||||
|
// ---------------------------------
|
||||||
// Update
|
// Update
|
||||||
|
|
||||||
update();
|
update();
|
||||||
@@ -160,30 +172,18 @@ int Application::run()
|
|||||||
m_window->update();
|
m_window->update();
|
||||||
m_scene->update(deltaTime);
|
m_scene->update(deltaTime);
|
||||||
|
|
||||||
|
// ---------------------------------
|
||||||
// Render
|
// Render
|
||||||
|
|
||||||
render();
|
|
||||||
|
|
||||||
RenderCommand::clearColor({ 0.2f, 0.3f, 0.3f, 1.0f });
|
|
||||||
RenderCommand::clear();
|
|
||||||
|
|
||||||
std::pair<glm::mat4, glm::mat4> projectionView = m_scene->cameraProjectionView();
|
|
||||||
RendererCubemap::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
|
|
||||||
Renderer2D::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
|
|
||||||
RendererCharacter::the().beginScene();
|
|
||||||
|
|
||||||
m_scene->render();
|
m_scene->render();
|
||||||
// RendererCharacter::the().drawCharacter(character, f->texture());
|
|
||||||
|
|
||||||
RendererCubemap::the().endScene();
|
|
||||||
Renderer2D::the().endScene();
|
|
||||||
RendererCharacter::the().endScene();
|
|
||||||
|
|
||||||
m_window->render();
|
m_window->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
ruc::debug("Application shutdown");
|
ruc::debug("Application shutdown");
|
||||||
|
|
||||||
|
ruc::debug("Average frametime: {:.2f}ms", (gametime / frames) * 1000);
|
||||||
|
|
||||||
return m_status;
|
return m_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@ bool Application::onWindowResize(WindowResizeEvent& e)
|
|||||||
{
|
{
|
||||||
ruc::info("WindowResizeEvent {}x{}", e.getWidth(), e.getHeight());
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -226,6 +226,11 @@ bool Application::onKeyPress(KeyPressEvent& e)
|
|||||||
m_window->setShouldClose(true);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 Riyyi
|
* Copyright (C) 2022,2024 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
@@ -8,18 +8,18 @@
|
|||||||
|
|
||||||
#include <memory> // std::unique_ptr, std::shared_ptr
|
#include <memory> // std::unique_ptr, std::shared_ptr
|
||||||
|
|
||||||
#include "ruc/singleton.h"
|
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
class Event;
|
class Event;
|
||||||
class Font;
|
class Font;
|
||||||
|
class Framebuffer;
|
||||||
class KeyPressEvent;
|
class KeyPressEvent;
|
||||||
class MousePositionEvent;
|
class MousePositionEvent;
|
||||||
class Scene;
|
class Scene;
|
||||||
class Window;
|
class Window;
|
||||||
class WindowCloseEvent;
|
class WindowCloseEvent;
|
||||||
class WindowResizeEvent;
|
class WindowResizeEvent;
|
||||||
|
struct TransformComponent;
|
||||||
|
|
||||||
class Application {
|
class Application {
|
||||||
public:
|
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
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 Riyyi
|
* Copyright (C) 2022-2024 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
@@ -7,32 +7,32 @@
|
|||||||
#include <array> // std::array
|
#include <array> // std::array
|
||||||
#include <charconv> // std;:from_chars
|
#include <charconv> // std;:from_chars
|
||||||
#include <cstdint> // int32_t, uint32_t
|
#include <cstdint> // int32_t, uint32_t
|
||||||
#include <limits> // std::numeric_limits
|
|
||||||
#include <ranges> // std::views::split
|
#include <ranges> // std::views::split
|
||||||
#include <string> // std::getline
|
#include <string> // std::getline
|
||||||
#include <utility> // std::move
|
|
||||||
|
|
||||||
#include "ruc/file.h"
|
#include "ruc/file.h"
|
||||||
#include "ruc/format/log.h"
|
|
||||||
#include "ruc/meta/assert.h"
|
#include "ruc/meta/assert.h"
|
||||||
#include "ruc/meta/concepts.h"
|
#include "ruc/meta/concepts.h"
|
||||||
|
|
||||||
#include "inferno/render/font.h"
|
#include "inferno/asset/font.h"
|
||||||
#include "inferno/render/texture.h"
|
#include "inferno/asset/texture.h"
|
||||||
#include "inferno/util/integer.h"
|
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
Font::Font(const std::string& name)
|
std::shared_ptr<Font> Font::create(std::string_view path)
|
||||||
: m_name(std::move(name))
|
|
||||||
{
|
{
|
||||||
std::string path = name + ".fnt";
|
auto result = std::shared_ptr<Font>(new Font(path));
|
||||||
std::string image = name + ".png";
|
|
||||||
|
|
||||||
std::string font = ruc::File(path).data();
|
auto stringPath = std::string(path);
|
||||||
parseFont(font);
|
std::string file = stringPath + ".fnt";
|
||||||
|
std::string image = stringPath + ".png";
|
||||||
|
|
||||||
m_texture = Texture2D::create(image);
|
std::string font = ruc::File(file).data();
|
||||||
|
result->parseFont(font);
|
||||||
|
|
||||||
|
result->m_texture = Texture2D::create(image);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Move this to ruc
|
// TODO: Move this to ruc
|
||||||
@@ -73,14 +73,14 @@ void Font::parseFont(const std::string& font)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Character
|
// Symbol
|
||||||
// ---------------------------------
|
// ---------------------------------
|
||||||
|
|
||||||
if (action.compare("char") == 0) {
|
if (action.compare("char") == 0) {
|
||||||
unsigned char id = convert<unsigned char>(findValue("id", columns));
|
auto id = convert<char>(findValue("id", columns));
|
||||||
uint32_t width = convert<uint32_t>(findValue("width", columns));
|
auto width = convert<uint32_t>(findValue("width", columns));
|
||||||
uint32_t height = convert<uint32_t>(findValue("height", columns));
|
auto height = convert<uint32_t>(findValue("height", columns));
|
||||||
Character character = {
|
Symbol symbol = {
|
||||||
.id = id,
|
.id = id,
|
||||||
.position = {
|
.position = {
|
||||||
convert<uint32_t>(findValue("x", columns)) + m_padding[Padding::Left],
|
convert<uint32_t>(findValue("x", columns)) + m_padding[Padding::Left],
|
||||||
@@ -97,7 +97,7 @@ void Font::parseFont(const std::string& font)
|
|||||||
.advance = convert<uint32_t>(findValue("xadvance", columns)) - m_padding[Padding::Left] - m_padding[Padding::Right]
|
.advance = convert<uint32_t>(findValue("xadvance", columns)) - m_padding[Padding::Left] - m_padding[Padding::Right]
|
||||||
};
|
};
|
||||||
|
|
||||||
m_characterList.emplace(id, std::make_shared<Character>(character));
|
m_symbolList.emplace(id, std::make_shared<Symbol>(symbol));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,10 +109,10 @@ void Font::parseFont(const std::string& font)
|
|||||||
unsigned char second = convert<unsigned char>(findValue("second", columns));
|
unsigned char second = convert<unsigned char>(findValue("second", columns));
|
||||||
char amount = convert<char>(findValue("amount", columns));
|
char amount = convert<char>(findValue("amount", columns));
|
||||||
|
|
||||||
// Add the kerning of the previous character to this character
|
// Add the kerning of the previous symbol to this symbol
|
||||||
if (m_characterList.find(second) != m_characterList.end()) {
|
if (m_symbolList.find(second) != m_symbolList.end()) {
|
||||||
auto character = m_characterList.at(second);
|
auto symbol = m_symbolList.at(second);
|
||||||
character->kernings.emplace(first, amount);
|
symbol->kernings.emplace(first, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -132,7 +132,7 @@ std::vector<std::string> Font::findColumns(const std::string& line) const
|
|||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
size_t find = 0;
|
size_t find = 0;
|
||||||
size_t findFirstNotOf = 0;
|
size_t findFirstNotOf = 0;
|
||||||
// Loop over line characters
|
// Loop over line symbols
|
||||||
while (find != std::string::npos) {
|
while (find != std::string::npos) {
|
||||||
find = line.find(" ", index);
|
find = line.find(" ", index);
|
||||||
findFirstNotOf = line.find_first_not_of(" ", index);
|
findFirstNotOf = line.find_first_not_of(" ", index);
|
||||||
@@ -167,58 +167,6 @@ std::string Font::findValue(const std::string& key, const std::vector<std::strin
|
|||||||
return "";
|
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
|
} // namespace Inferno
|
||||||
|
|
||||||
void ruc::format::Formatter<glm::ivec2>::format(Builder& builder, glm::ivec2 value) const
|
void ruc::format::Formatter<glm::ivec2>::format(Builder& builder, glm::ivec2 value) const
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 Riyyi
|
* Copyright (C) 2022-2024 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
@@ -10,13 +10,15 @@
|
|||||||
#include <cstdint> // int32_t, uint32_t
|
#include <cstdint> // int32_t, uint32_t
|
||||||
#include <memory> // std::shared_ptr
|
#include <memory> // std::shared_ptr
|
||||||
#include <string> // std::string
|
#include <string> // std::string
|
||||||
|
#include <string_view>
|
||||||
#include <unordered_map> // std::unordered_map
|
#include <unordered_map> // std::unordered_map
|
||||||
#include <vector> // std::vector
|
#include <vector> // std::vector
|
||||||
|
|
||||||
#include "glm/ext/vector_int2.hpp" // glm::ivec2
|
#include "glm/ext/vector_int2.hpp" // glm::ivec2
|
||||||
#include "glm/ext/vector_uint2.hpp" // glm::uvec2
|
#include "glm/ext/vector_uint2.hpp" // glm::uvec2
|
||||||
#include "ruc/format/format.h"
|
#include "ruc/format/format.h"
|
||||||
#include "ruc/singleton.h"
|
|
||||||
|
#include "inferno/asset/asset-manager.h"
|
||||||
|
|
||||||
#define PADDING 3
|
#define PADDING 3
|
||||||
|
|
||||||
@@ -24,22 +26,24 @@ namespace Inferno {
|
|||||||
|
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
struct Character {
|
struct Symbol {
|
||||||
char id; // Character
|
char id; // Symbol
|
||||||
glm::uvec2 position; // Position
|
glm::uvec2 position; // Position
|
||||||
glm::uvec2 size; // Width/height
|
glm::uvec2 size; // Width/height
|
||||||
glm::ivec2 offset; // Offset from baseline to left / top of glyph
|
glm::ivec2 offset; // Offset from baseline to left / top of glyph
|
||||||
uint32_t advance; // Amount to advance to next glyph
|
uint32_t advance; // Amount to advance to next glyph
|
||||||
std::unordered_map<unsigned char, char> kernings; // Kernings for characters that come before this one
|
std::unordered_map<unsigned char, char> kernings; // Kernings for symbols that come before this one
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
|
|
||||||
class Font {
|
class Font final : public Asset {
|
||||||
public:
|
public:
|
||||||
Font(const std::string& name);
|
|
||||||
virtual ~Font() {}
|
virtual ~Font() {}
|
||||||
|
|
||||||
|
// Factory function
|
||||||
|
static std::shared_ptr<Font> create(std::string_view path);
|
||||||
|
|
||||||
enum Padding {
|
enum Padding {
|
||||||
Top = 0,
|
Top = 0,
|
||||||
Right,
|
Right,
|
||||||
@@ -47,46 +51,40 @@ public:
|
|||||||
Left,
|
Left,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::string name() const { return m_name; }
|
|
||||||
inline uint32_t size() const { return m_size; }
|
inline uint32_t size() const { return m_size; }
|
||||||
inline uint32_t lineSpacing() const { return m_lineSpacing; }
|
inline uint32_t lineSpacing() const { return m_lineSpacing; }
|
||||||
inline std::shared_ptr<Texture> texture() const { return m_texture; }
|
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<Symbol> get(unsigned char c) const { return m_symbolList.at(c); }
|
||||||
inline std::shared_ptr<Character> operator[](unsigned char c) const { return m_characterList.at(c); }
|
inline std::shared_ptr<Symbol> operator[](unsigned char c) const { return m_symbolList.at(c); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Font(std::string_view path)
|
||||||
|
: Asset(path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void parseFont(const std::string& font);
|
void parseFont(const std::string& font);
|
||||||
std::string findAction(const std::string& line) const;
|
std::string findAction(const std::string& line) const;
|
||||||
std::vector<std::string> findColumns(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;
|
std::string findValue(const std::string& key, const std::vector<std::string>& columns) const;
|
||||||
|
|
||||||
std::string m_name;
|
virtual bool isFont() const override { return true; }
|
||||||
|
|
||||||
|
private:
|
||||||
unsigned char m_size = { 0 };
|
unsigned char m_size = { 0 };
|
||||||
uint32_t m_lineSpacing = { 0 };
|
uint32_t m_lineSpacing = { 0 };
|
||||||
std::array<uint32_t, 4> m_padding = { 0 };
|
std::array<uint32_t, 4> m_padding = { 0 };
|
||||||
std::shared_ptr<Texture> m_texture;
|
std::shared_ptr<Texture> m_texture;
|
||||||
std::unordered_map<unsigned char, std::shared_ptr<Character>> m_characterList;
|
std::unordered_map<unsigned char, std::shared_ptr<Symbol>> m_symbolList;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
class FontManager final : public ruc::Singleton<FontManager> {
|
// clang-format off
|
||||||
public:
|
template<>
|
||||||
FontManager(s);
|
inline bool Asset::fastIs<Font>() const { return isFont(); }
|
||||||
virtual ~FontManager();
|
// clang-format on
|
||||||
|
|
||||||
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
|
} // namespace Inferno
|
||||||
|
|
||||||
@@ -98,7 +96,7 @@ struct ruc::format::Formatter<glm::ivec2> : Formatter<std::vector<int32_t>> {
|
|||||||
// FontManager fm;
|
// FontManager fm;
|
||||||
// Font f = fm.load("path/to/font");
|
// Font f = fm.load("path/to/font");
|
||||||
// Font f2("path/to/font");
|
// Font f2("path/to/font");
|
||||||
// Character c = f['a'];
|
// Symbol c = f['a'];
|
||||||
|
|
||||||
// Look into using signed distance fields for texture map generation ? anti-aliasing for text
|
// Look into using signed distance fields for texture map generation ? anti-aliasing for text
|
||||||
// https://youtu.be/d8cfgcJR9Tk
|
// 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
|
||||||
@@ -7,15 +7,16 @@
|
|||||||
#include <climits> // UINT_MAX
|
#include <climits> // UINT_MAX
|
||||||
#include <cstdint> // uint8_t, uint32_t
|
#include <cstdint> // uint8_t, uint32_t
|
||||||
#include <memory> // std::shared_ptr
|
#include <memory> // std::shared_ptr
|
||||||
#include <utility> // std::move
|
|
||||||
|
|
||||||
|
#include "assimp/texture.h"
|
||||||
#include "glad/glad.h"
|
#include "glad/glad.h"
|
||||||
#include "ruc/format/log.h"
|
|
||||||
#include "ruc/meta/assert.h"
|
#include "ruc/meta/assert.h"
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include "stb/stb_image.h"
|
#include "stb/stb_image.h"
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include "stb/stb_image_write.h"
|
||||||
|
|
||||||
#include "inferno/render/texture.h"
|
#include "inferno/asset/texture.h"
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
@@ -24,21 +25,73 @@ Texture::~Texture()
|
|||||||
glDeleteTextures(1, &m_id);
|
glDeleteTextures(1, &m_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::init(uint32_t width, uint32_t height, uint8_t channels)
|
// -----------------------------------------
|
||||||
{
|
|
||||||
m_width = width;
|
|
||||||
m_height = height;
|
|
||||||
|
|
||||||
m_internalFormat = (channels == 3) ? GL_RGB8 : GL_RGBA8;
|
void Texture::savePNG(std::string_view path, std::shared_ptr<Texture> texture)
|
||||||
m_dataFormat = (channels == 3) ? GL_RGB : GL_RGBA;
|
{
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
std::shared_ptr<Texture> Texture2D::create(const std::string& path)
|
void Texture::init(uint32_t width, uint32_t height, uint8_t channels)
|
||||||
{
|
{
|
||||||
auto result = std::shared_ptr<Texture2D>(new Texture2D);
|
init(width, height,
|
||||||
result->m_path = path;
|
(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 width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
@@ -47,11 +100,11 @@ std::shared_ptr<Texture> Texture2D::create(const std::string& path)
|
|||||||
|
|
||||||
// Load image data
|
// Load image data
|
||||||
stbi_set_flip_vertically_on_load(1);
|
stbi_set_flip_vertically_on_load(1);
|
||||||
data = stbi_load(path.c_str(), &width, &height, &channels, STBI_default);
|
data = stbi_load(path.data(), &width, &height, &channels, STBI_default);
|
||||||
VERIFY(data, "failed to load image: '{}'", path);
|
VERIFY(data, "failed to load image: '{}'", path);
|
||||||
|
|
||||||
result->init(width, height, channels);
|
result->init(width, height, channels);
|
||||||
result->create(data);
|
result->createImpl(data);
|
||||||
|
|
||||||
// Clean resources
|
// Clean resources
|
||||||
stbi_image_free(data);
|
stbi_image_free(data);
|
||||||
@@ -59,6 +112,32 @@ std::shared_ptr<Texture> Texture2D::create(const std::string& path)
|
|||||||
return result;
|
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
|
void Texture2D::bind(uint32_t unit) const
|
||||||
{
|
{
|
||||||
// Set active unit
|
// Set active unit
|
||||||
@@ -75,7 +154,7 @@ void Texture2D::unbind() const
|
|||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture2D::create(unsigned char* data)
|
void Texture2D::createImpl(unsigned char* data)
|
||||||
{
|
{
|
||||||
m_id = UINT_MAX;
|
m_id = UINT_MAX;
|
||||||
|
|
||||||
@@ -97,7 +176,7 @@ void Texture2D::create(unsigned char* data)
|
|||||||
m_width, m_height, // Image width/height
|
m_width, m_height, // Image width/height
|
||||||
0, // Always 0 (legacy)
|
0, // Always 0 (legacy)
|
||||||
m_dataFormat, // Texture source format
|
m_dataFormat, // Texture source format
|
||||||
GL_UNSIGNED_BYTE, // Texture source datatype
|
m_dataType, // Texture source datatype
|
||||||
data); // Image data
|
data); // Image data
|
||||||
|
|
||||||
// Set the texture wrapping / filtering options
|
// Set the texture wrapping / filtering options
|
||||||
@@ -115,12 +194,11 @@ void Texture2D::create(unsigned char* data)
|
|||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
std::shared_ptr<Texture> TextureCubemap::create(const std::string& path)
|
std::shared_ptr<TextureCubemap> TextureCubemap::create(std::string_view path)
|
||||||
{
|
{
|
||||||
auto result = std::shared_ptr<TextureCubemap>(new TextureCubemap);
|
auto result = std::shared_ptr<TextureCubemap>(new TextureCubemap(path));
|
||||||
result->m_path = path;
|
|
||||||
|
|
||||||
result->create();
|
result->createImpl();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -141,7 +219,7 @@ void TextureCubemap::unbind() const
|
|||||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureCubemap::create()
|
void TextureCubemap::createImpl()
|
||||||
{
|
{
|
||||||
m_id = UINT_MAX;
|
m_id = UINT_MAX;
|
||||||
|
|
||||||
@@ -183,7 +261,7 @@ void TextureCubemap::create()
|
|||||||
m_width, m_height, // Image width/height
|
m_width, m_height, // Image width/height
|
||||||
0, // Always 0 (legacy)
|
0, // Always 0 (legacy)
|
||||||
m_dataFormat, // Texture source format
|
m_dataFormat, // Texture source format
|
||||||
GL_UNSIGNED_BYTE, // Texture source datatype
|
m_dataType, // Texture source datatype
|
||||||
data); // Image data
|
data); // Image data
|
||||||
|
|
||||||
// Clean resources
|
// Clean resources
|
||||||
@@ -203,55 +281,60 @@ void TextureCubemap::create()
|
|||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
TextureManager::TextureManager(s)
|
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)
|
||||||
{
|
{
|
||||||
ruc::info("TextureManager initialized");
|
auto result = std::shared_ptr<TextureFramebuffer>(new TextureFramebuffer(path));
|
||||||
|
|
||||||
|
result->init(width, height, internalFormat, dataFormat, dataType);
|
||||||
|
result->createImpl();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureManager::~TextureManager()
|
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 TextureManager::add(const std::string& path, std::shared_ptr<Texture> texture)
|
void TextureFramebuffer::unbind() const
|
||||||
{
|
{
|
||||||
// Construct (key, value) pair and insert it into the unordered_map
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
m_textureList.emplace(std::move(path), std::move(texture));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Texture> TextureManager::load(const std::string& path, Texture::Type type)
|
void TextureFramebuffer::createImpl()
|
||||||
{
|
{
|
||||||
if (exists(path)) {
|
m_id = UINT_MAX;
|
||||||
return get(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto texture = (type == Texture::TwoDimensional) ? Texture2D::create(path) : TextureCubemap::create(path);
|
// Create texture object
|
||||||
add(path, texture);
|
glGenTextures(1, &m_id);
|
||||||
|
|
||||||
return texture;
|
// Bind texture object
|
||||||
}
|
glBindTexture(GL_TEXTURE_2D, m_id);
|
||||||
|
|
||||||
std::shared_ptr<Texture> TextureManager::get(const std::string& path)
|
// Generate texture
|
||||||
{
|
glTexImage2D(
|
||||||
return exists(path) ? m_textureList.at(path) : nullptr;
|
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
|
||||||
|
|
||||||
bool TextureManager::exists(const std::string& path)
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
{
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
return m_textureList.find(path) != m_textureList.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureManager::remove(const std::string& path)
|
// Unbind texture object
|
||||||
{
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
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
|
} // 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
|
||||||
@@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
#include "ruc/json/json.h"
|
#include "ruc/json/json.h"
|
||||||
|
|
||||||
|
#include "inferno/asset/texture.h"
|
||||||
#include "inferno/component/cubemap-component.h"
|
#include "inferno/component/cubemap-component.h"
|
||||||
#include "inferno/component/spritecomponent.h" // TODO: Move glm::x toJson/fromJson to separate file
|
#include "inferno/component/serialize.h" // not detected as used by clang-tidy
|
||||||
#include "inferno/render/texture.h"
|
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
@@ -20,7 +20,10 @@ void fromJson(const ruc::Json& json, CubemapComponent& value)
|
|||||||
json.at("color").getTo(value.color);
|
json.at("color").getTo(value.color);
|
||||||
}
|
}
|
||||||
if (json.exists("texture") && json.at("texture").type() == ruc::Json::Type::String) {
|
if (json.exists("texture") && json.at("texture").type() == ruc::Json::Type::String) {
|
||||||
value.texture = TextureManager::the().load(json.at("texture").asString(), Texture::Type::Cubemap);
|
value.texture = AssetManager::the().load<TextureCubemap>(json.at("texture").asString());
|
||||||
|
}
|
||||||
|
if (json.exists("isLight")) {
|
||||||
|
json.at("isLight").getTo(value.isLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,14 @@
|
|||||||
#include "glm/ext/vector_float4.hpp" // glm::vec4
|
#include "glm/ext/vector_float4.hpp" // glm::vec4
|
||||||
#include "ruc/json/json.h"
|
#include "ruc/json/json.h"
|
||||||
|
|
||||||
#include "inferno/render/texture.h"
|
#include "inferno/asset/texture.h"
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
struct CubemapComponent {
|
struct CubemapComponent {
|
||||||
glm::vec4 color { 1.0f };
|
glm::vec4 color { 1.0f };
|
||||||
std::shared_ptr<Texture> texture;
|
std::shared_ptr<Texture> texture;
|
||||||
|
bool isLight { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
void fromJson(const ruc::Json& json, CubemapComponent& value);
|
void fromJson(const ruc::Json& json, CubemapComponent& value);
|
||||||
|
|||||||
@@ -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,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
|
* 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/component/spritecomponent.h"
|
||||||
#include "inferno/render/texture.h"
|
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
@@ -17,32 +21,8 @@ void fromJson(const ruc::Json& json, SpriteComponent& value)
|
|||||||
json.at("color").getTo(value.color);
|
json.at("color").getTo(value.color);
|
||||||
}
|
}
|
||||||
if (json.exists("texture") && json.at("texture").type() == ruc::Json::Type::String) {
|
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 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 "glm/ext/vector_float4.hpp" // glm::vec4
|
||||||
#include "ruc/json/json.h"
|
#include "ruc/json/json.h"
|
||||||
|
|
||||||
#include "inferno/render/texture.h"
|
#include "inferno/asset/texture.h"
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
@@ -23,10 +23,3 @@ struct SpriteComponent {
|
|||||||
void fromJson(const ruc::Json& json, SpriteComponent& value);
|
void fromJson(const ruc::Json& json, SpriteComponent& value);
|
||||||
|
|
||||||
} // namespace Inferno
|
} // namespace Inferno
|
||||||
|
|
||||||
namespace glm {
|
|
||||||
|
|
||||||
void toJson(ruc::Json& json, const vec4& value);
|
|
||||||
void fromJson(const ruc::Json& json, vec4& value);
|
|
||||||
|
|
||||||
} // namespace glm
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "ruc/format/format.h"
|
#include "ruc/format/format.h"
|
||||||
#include "ruc/json/json.h"
|
#include "ruc/json/json.h"
|
||||||
|
|
||||||
|
#include "inferno/component/serialize.h"
|
||||||
#include "inferno/component/transformcomponent.h"
|
#include "inferno/component/transformcomponent.h"
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
@@ -28,56 +29,6 @@ void fromJson(const ruc::Json& json, TransformComponent& value)
|
|||||||
|
|
||||||
} // namespace Inferno
|
} // 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
|
void ruc::format::Formatter<Inferno::TransformComponent>::format(Builder& builder, Inferno::TransformComponent value) const
|
||||||
{
|
{
|
||||||
builder.putString("transform ");
|
builder.putString("transform ");
|
||||||
|
|||||||
@@ -6,9 +6,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint> // uint32_t
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "entt/entity/entity.hpp" // entt::null
|
#include "entt/entity/entity.hpp" // entt::null
|
||||||
#include "entt/entity/fwd.hpp" // entt::entity
|
#include "entt/entity/fwd.hpp" // entt::entity
|
||||||
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
|
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
|
||||||
@@ -31,33 +28,6 @@ void fromJson(const ruc::Json& json, TransformComponent& value);
|
|||||||
|
|
||||||
} // namespace Inferno
|
} // 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<>
|
template<>
|
||||||
struct ruc::format::Formatter<Inferno::TransformComponent> : Formatter<glm::vec3> {
|
struct ruc::format::Formatter<Inferno::TransformComponent> : Formatter<glm::vec3> {
|
||||||
void format(Builder& builder, Inferno::TransformComponent value) const;
|
void format(Builder& builder, Inferno::TransformComponent value) const;
|
||||||
|
|||||||
@@ -14,24 +14,27 @@ namespace Inferno {
|
|||||||
|
|
||||||
class KeyEvent : public Event {
|
class KeyEvent : public Event {
|
||||||
public:
|
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)
|
EVENT_CLASS_CATEGORY(InputEventCategory | KeyEventCategory)
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
KeyEvent(int key)
|
KeyEvent(int key, int mods)
|
||||||
: m_key(key)
|
: m_key(key)
|
||||||
|
, m_mods(mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_key;
|
int m_key { 0 };
|
||||||
|
int m_mods { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeyPressEvent : public KeyEvent {
|
class KeyPressEvent final : public KeyEvent {
|
||||||
public:
|
public:
|
||||||
KeyPressEvent(int key)
|
KeyPressEvent(int key, int mods)
|
||||||
: KeyEvent(key)
|
: KeyEvent(key, mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,10 +48,10 @@ public:
|
|||||||
EVENT_CLASS_TYPE(KeyPress)
|
EVENT_CLASS_TYPE(KeyPress)
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeyReleaseEvent : public KeyEvent {
|
class KeyReleaseEvent final : public KeyEvent {
|
||||||
public:
|
public:
|
||||||
KeyReleaseEvent(int key)
|
KeyReleaseEvent(int key, int mods)
|
||||||
: KeyEvent(key)
|
: KeyEvent(key, mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,13 +62,13 @@ public:
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
EVENT_CLASS_TYPE(KeyPress)
|
EVENT_CLASS_TYPE(KeyRelease)
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeyRepeatEvent : public KeyEvent {
|
class KeyRepeatEvent final : public KeyEvent {
|
||||||
public:
|
public:
|
||||||
KeyRepeatEvent(int key)
|
KeyRepeatEvent(int key, int mods)
|
||||||
: KeyEvent(key)
|
: KeyEvent(key, mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +79,7 @@ public:
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
EVENT_CLASS_TYPE(KeyPress)
|
EVENT_CLASS_TYPE(KeyRepeat)
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Inferno
|
} // namespace Inferno
|
||||||
|
|||||||
@@ -139,14 +139,33 @@ static std::unordered_map<std::string_view, int> keys({
|
|||||||
{ MAP_KEY(GLFW_KEY_MENU) },
|
{ 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)
|
int keyCode(std::string_view name)
|
||||||
{
|
{
|
||||||
VERIFY(keys.find(name) != keys.end(), "could not find key code: {}", name);
|
VERIFY(keys.find(name) != keys.end(), "could not find key code: {}", name);
|
||||||
return keys.at(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)
|
std::string_view keyName(int key)
|
||||||
{
|
{
|
||||||
auto it = std::find_if(keys.begin(), keys.end(), [key](const auto& keybind) {
|
auto it = std::find_if(keys.begin(), keys.end(), [key](const auto& keybind) {
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
int keyCode(std::string_view name);
|
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
|
} // namespace Inferno
|
||||||
|
|||||||
+177
-116
@@ -4,12 +4,17 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* 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 "glad/glad.h"
|
||||||
#include "inferno/render/buffer.h"
|
|
||||||
#include "ruc/meta/assert.h"
|
#include "ruc/meta/assert.h"
|
||||||
|
|
||||||
|
#include "inferno/render/buffer.h"
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
@@ -37,113 +42,78 @@ uint32_t BufferElement::getTypeGL() const
|
|||||||
return BufferElement::getTypeGL(m_type);
|
return BufferElement::getTypeGL(m_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t BufferElement::getTypeSize(const BufferElementType type)
|
uint32_t BufferElement::getTypeSize(BufferElementType type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BufferElementType::None:
|
case BufferElementType::None:
|
||||||
return 0;
|
return 0;
|
||||||
case BufferElementType::Bool:
|
case BufferElementType::Bool:
|
||||||
return sizeof(bool);
|
|
||||||
case BufferElementType::Bool2:
|
case BufferElementType::Bool2:
|
||||||
return sizeof(bool) * 2;
|
|
||||||
case BufferElementType::Bool3:
|
case BufferElementType::Bool3:
|
||||||
return sizeof(bool) * 3;
|
|
||||||
case BufferElementType::Bool4:
|
case BufferElementType::Bool4:
|
||||||
return sizeof(bool) * 4;
|
return sizeof(bool) * getTypeCount(type);
|
||||||
case BufferElementType::Int:
|
case BufferElementType::Int:
|
||||||
return sizeof(int32_t);
|
|
||||||
case BufferElementType::Int2:
|
case BufferElementType::Int2:
|
||||||
return sizeof(int32_t) * 2;
|
|
||||||
case BufferElementType::Int3:
|
case BufferElementType::Int3:
|
||||||
return sizeof(int32_t) * 3;
|
|
||||||
case BufferElementType::Int4:
|
case BufferElementType::Int4:
|
||||||
return sizeof(int32_t) * 4;
|
return sizeof(int32_t) * getTypeCount(type);
|
||||||
case BufferElementType::Uint:
|
case BufferElementType::Uint:
|
||||||
return sizeof(uint32_t);
|
|
||||||
case BufferElementType::Uint2:
|
case BufferElementType::Uint2:
|
||||||
return sizeof(uint32_t) * 2;
|
|
||||||
case BufferElementType::Uint3:
|
case BufferElementType::Uint3:
|
||||||
return sizeof(uint32_t) * 3;
|
|
||||||
case BufferElementType::Uint4:
|
case BufferElementType::Uint4:
|
||||||
return sizeof(uint32_t) * 4;
|
return sizeof(uint32_t) * getTypeCount(type);
|
||||||
case BufferElementType::Float:
|
case BufferElementType::Float:
|
||||||
return sizeof(float);
|
|
||||||
case BufferElementType::Vec2:
|
case BufferElementType::Vec2:
|
||||||
return sizeof(float) * 2;
|
|
||||||
case BufferElementType::Vec3:
|
case BufferElementType::Vec3:
|
||||||
return sizeof(float) * 3;
|
|
||||||
case BufferElementType::Vec4:
|
case BufferElementType::Vec4:
|
||||||
return sizeof(float) * 4;
|
return sizeof(float) * getTypeCount(type);
|
||||||
case BufferElementType::Double:
|
case BufferElementType::Double:
|
||||||
return sizeof(double);
|
case BufferElementType::Vec2Double:
|
||||||
case BufferElementType::Double2:
|
case BufferElementType::Vec3Double:
|
||||||
return sizeof(double);
|
case BufferElementType::Vec4Double:
|
||||||
case BufferElementType::Double3:
|
return sizeof(double) * getTypeCount(type);
|
||||||
return sizeof(double);
|
|
||||||
case BufferElementType::Double4:
|
|
||||||
return sizeof(double);
|
|
||||||
case BufferElementType::Mat2:
|
case BufferElementType::Mat2:
|
||||||
return sizeof(float) * 2 * 2;
|
|
||||||
case BufferElementType::Mat3:
|
case BufferElementType::Mat3:
|
||||||
return sizeof(float) * 3 * 3;
|
|
||||||
case BufferElementType::Mat4:
|
case BufferElementType::Mat4:
|
||||||
return sizeof(float) * 4 * 4;
|
return sizeof(float) * getTypeCount(type);
|
||||||
case BufferElementType::DoubleMat2:
|
case BufferElementType::MatDouble2:
|
||||||
return sizeof(double) * 2 * 2;
|
case BufferElementType::MatDouble3:
|
||||||
case BufferElementType::DoubleMat3:
|
case BufferElementType::MatDouble4:
|
||||||
return sizeof(double) * 3 * 3;
|
return sizeof(double) * getTypeCount(type);
|
||||||
case BufferElementType::DoubleMat4:
|
|
||||||
return sizeof(double) * 4 * 4;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
VERIFY(false, "BufferElement unknown BufferElementType size!");
|
VERIFY(false, "BufferElement unknown BufferElementType size!");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t BufferElement::getTypeCount(const BufferElementType type)
|
uint32_t BufferElement::getTypeCount(BufferElementType type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BufferElementType::None:
|
case BufferElementType::None:
|
||||||
return 0;
|
return 0;
|
||||||
case BufferElementType::Bool:
|
case BufferElementType::Bool:
|
||||||
return 1;
|
|
||||||
case BufferElementType::Bool2:
|
|
||||||
return 2;
|
|
||||||
case BufferElementType::Bool3:
|
|
||||||
return 3;
|
|
||||||
case BufferElementType::Bool4:
|
|
||||||
return 4;
|
|
||||||
case BufferElementType::Int:
|
case BufferElementType::Int:
|
||||||
return 1;
|
|
||||||
case BufferElementType::Int2:
|
|
||||||
return 2;
|
|
||||||
case BufferElementType::Int3:
|
|
||||||
return 3;
|
|
||||||
case BufferElementType::Int4:
|
|
||||||
return 4;
|
|
||||||
case BufferElementType::Uint:
|
case BufferElementType::Uint:
|
||||||
return 1;
|
|
||||||
case BufferElementType::Uint2:
|
|
||||||
return 2;
|
|
||||||
case BufferElementType::Uint3:
|
|
||||||
return 3;
|
|
||||||
case BufferElementType::Uint4:
|
|
||||||
return 4;
|
|
||||||
case BufferElementType::Float:
|
case BufferElementType::Float:
|
||||||
return 1;
|
|
||||||
case BufferElementType::Vec2:
|
|
||||||
return 2;
|
|
||||||
case BufferElementType::Vec3:
|
|
||||||
return 3;
|
|
||||||
case BufferElementType::Vec4:
|
|
||||||
return 4;
|
|
||||||
case BufferElementType::Double:
|
case BufferElementType::Double:
|
||||||
return 1;
|
return 1;
|
||||||
case BufferElementType::Double2:
|
case BufferElementType::Bool2:
|
||||||
|
case BufferElementType::Int2:
|
||||||
|
case BufferElementType::Uint2:
|
||||||
|
case BufferElementType::Vec2:
|
||||||
|
case BufferElementType::Vec2Double:
|
||||||
return 2;
|
return 2;
|
||||||
case BufferElementType::Double3:
|
case BufferElementType::Bool3:
|
||||||
|
case BufferElementType::Int3:
|
||||||
|
case BufferElementType::Uint3:
|
||||||
|
case BufferElementType::Vec3:
|
||||||
|
case BufferElementType::Vec3Double:
|
||||||
return 3;
|
return 3;
|
||||||
case BufferElementType::Double4:
|
case BufferElementType::Bool4:
|
||||||
|
case BufferElementType::Int4:
|
||||||
|
case BufferElementType::Uint4:
|
||||||
|
case BufferElementType::Vec4:
|
||||||
|
case BufferElementType::Vec4Double:
|
||||||
return 4;
|
return 4;
|
||||||
case BufferElementType::Mat2:
|
case BufferElementType::Mat2:
|
||||||
return 2 * 2;
|
return 2 * 2;
|
||||||
@@ -151,11 +121,11 @@ uint32_t BufferElement::getTypeCount(const BufferElementType type)
|
|||||||
return 3 * 3;
|
return 3 * 3;
|
||||||
case BufferElementType::Mat4:
|
case BufferElementType::Mat4:
|
||||||
return 4 * 4;
|
return 4 * 4;
|
||||||
case BufferElementType::DoubleMat2:
|
case BufferElementType::MatDouble2:
|
||||||
return 2 * 2;
|
return 2 * 2;
|
||||||
case BufferElementType::DoubleMat3:
|
case BufferElementType::MatDouble3:
|
||||||
return 3 * 3;
|
return 3 * 3;
|
||||||
case BufferElementType::DoubleMat4:
|
case BufferElementType::MatDouble4:
|
||||||
return 4 * 4;
|
return 4 * 4;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -163,62 +133,43 @@ uint32_t BufferElement::getTypeCount(const BufferElementType type)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t BufferElement::getTypeGL(const BufferElementType type)
|
uint32_t BufferElement::getTypeGL(BufferElementType type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BufferElementType::None:
|
case BufferElementType::None:
|
||||||
return GL_NONE;
|
return GL_NONE;
|
||||||
case BufferElementType::Bool:
|
case BufferElementType::Bool:
|
||||||
return GL_BOOL;
|
|
||||||
case BufferElementType::Bool2:
|
case BufferElementType::Bool2:
|
||||||
return GL_BOOL;
|
|
||||||
case BufferElementType::Bool3:
|
case BufferElementType::Bool3:
|
||||||
return GL_BOOL;
|
|
||||||
case BufferElementType::Bool4:
|
case BufferElementType::Bool4:
|
||||||
return GL_BOOL;
|
return GL_BOOL;
|
||||||
case BufferElementType::Int:
|
case BufferElementType::Int:
|
||||||
return GL_INT;
|
|
||||||
case BufferElementType::Int2:
|
case BufferElementType::Int2:
|
||||||
return GL_INT;
|
|
||||||
case BufferElementType::Int3:
|
case BufferElementType::Int3:
|
||||||
return GL_INT;
|
|
||||||
case BufferElementType::Int4:
|
case BufferElementType::Int4:
|
||||||
return GL_INT;
|
return GL_INT;
|
||||||
case BufferElementType::Uint:
|
case BufferElementType::Uint:
|
||||||
return GL_UNSIGNED_INT;
|
|
||||||
case BufferElementType::Uint2:
|
case BufferElementType::Uint2:
|
||||||
return GL_UNSIGNED_INT;
|
|
||||||
case BufferElementType::Uint3:
|
case BufferElementType::Uint3:
|
||||||
return GL_UNSIGNED_INT;
|
|
||||||
case BufferElementType::Uint4:
|
case BufferElementType::Uint4:
|
||||||
return GL_UNSIGNED_INT;
|
return GL_UNSIGNED_INT;
|
||||||
case BufferElementType::Float:
|
case BufferElementType::Float:
|
||||||
return GL_FLOAT;
|
|
||||||
case BufferElementType::Vec2:
|
case BufferElementType::Vec2:
|
||||||
return GL_FLOAT;
|
|
||||||
case BufferElementType::Vec3:
|
case BufferElementType::Vec3:
|
||||||
return GL_FLOAT;
|
|
||||||
case BufferElementType::Vec4:
|
case BufferElementType::Vec4:
|
||||||
return GL_FLOAT;
|
return GL_FLOAT;
|
||||||
case BufferElementType::Double:
|
case BufferElementType::Double:
|
||||||
return GL_DOUBLE;
|
case BufferElementType::Vec2Double:
|
||||||
case BufferElementType::Double2:
|
case BufferElementType::Vec3Double:
|
||||||
return GL_DOUBLE;
|
case BufferElementType::Vec4Double:
|
||||||
case BufferElementType::Double3:
|
|
||||||
return GL_DOUBLE;
|
|
||||||
case BufferElementType::Double4:
|
|
||||||
return GL_DOUBLE;
|
return GL_DOUBLE;
|
||||||
case BufferElementType::Mat2:
|
case BufferElementType::Mat2:
|
||||||
return GL_FLOAT;
|
|
||||||
case BufferElementType::Mat3:
|
case BufferElementType::Mat3:
|
||||||
return GL_FLOAT;
|
|
||||||
case BufferElementType::Mat4:
|
case BufferElementType::Mat4:
|
||||||
return GL_FLOAT;
|
return GL_FLOAT;
|
||||||
case BufferElementType::DoubleMat2:
|
case BufferElementType::MatDouble2:
|
||||||
return GL_DOUBLE;
|
case BufferElementType::MatDouble3:
|
||||||
case BufferElementType::DoubleMat3:
|
case BufferElementType::MatDouble4:
|
||||||
return GL_DOUBLE;
|
|
||||||
case BufferElementType::DoubleMat4:
|
|
||||||
return GL_DOUBLE;
|
return GL_DOUBLE;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -226,6 +177,58 @@ uint32_t BufferElement::getTypeGL(const BufferElementType type)
|
|||||||
return 0;
|
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)
|
BufferLayout::BufferLayout(const std::initializer_list<BufferElement>& elements)
|
||||||
@@ -239,30 +242,25 @@ void BufferLayout::calculateOffsetsAndStride()
|
|||||||
m_stride = 0;
|
m_stride = 0;
|
||||||
for (auto& element : m_elements) {
|
for (auto& element : m_elements) {
|
||||||
element.setOffset(m_stride);
|
element.setOffset(m_stride);
|
||||||
m_stride += element.getSize();
|
m_stride += element.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
VertexBuffer::VertexBuffer(size_t 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);
|
glGenBuffers(1, &m_id);
|
||||||
bind();
|
bind();
|
||||||
|
|
||||||
// Upload data to the GPU
|
// Upload data to the GPU
|
||||||
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_DYNAMIC_DRAW);
|
||||||
|
|
||||||
unbind();
|
unbind();
|
||||||
}
|
}
|
||||||
@@ -297,11 +295,12 @@ void VertexBuffer::uploadData(const void* data, uint32_t size)
|
|||||||
IndexBuffer::IndexBuffer(uint32_t* indices, size_t size)
|
IndexBuffer::IndexBuffer(uint32_t* indices, size_t size)
|
||||||
: m_count(size / sizeof(uint32_t))
|
: m_count(size / sizeof(uint32_t))
|
||||||
{
|
{
|
||||||
|
m_id = UINT_MAX;
|
||||||
glCreateBuffers(1, &m_id);
|
glCreateBuffers(1, &m_id);
|
||||||
bind();
|
bind();
|
||||||
|
|
||||||
// Upload data to the GPU
|
// 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();
|
unbind();
|
||||||
}
|
}
|
||||||
@@ -321,10 +320,21 @@ void IndexBuffer::unbind() const
|
|||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
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()
|
VertexArray::VertexArray()
|
||||||
{
|
{
|
||||||
|
m_id = UINT_MAX;
|
||||||
glCreateVertexArrays(1, &m_id);
|
glCreateVertexArrays(1, &m_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,8 +355,8 @@ void VertexArray::unbind() const
|
|||||||
|
|
||||||
void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
|
void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
|
||||||
{
|
{
|
||||||
const auto& layout = vertexBuffer->getLayout();
|
const auto& layout = vertexBuffer->layout();
|
||||||
VERIFY(layout.getElements().size(), "VertexBuffer has no layout");
|
VERIFY(layout.elements().size(), "VertexBuffer has no layout");
|
||||||
|
|
||||||
bind();
|
bind();
|
||||||
vertexBuffer->bind();
|
vertexBuffer->bind();
|
||||||
@@ -354,20 +364,71 @@ void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
|
|||||||
uint32_t index = 0;
|
uint32_t index = 0;
|
||||||
for (const auto& element : layout) {
|
for (const auto& element : layout) {
|
||||||
glEnableVertexAttribArray(index);
|
glEnableVertexAttribArray(index);
|
||||||
|
switch (element.type()) {
|
||||||
|
case BufferElementType::None:
|
||||||
|
break;
|
||||||
|
case BufferElementType::Int:
|
||||||
|
case BufferElementType::Int2:
|
||||||
|
case BufferElementType::Int3:
|
||||||
|
case BufferElementType::Int4:
|
||||||
|
case BufferElementType::Uint:
|
||||||
|
case BufferElementType::Uint2:
|
||||||
|
case BufferElementType::Uint3:
|
||||||
|
case BufferElementType::Uint4: {
|
||||||
|
glVertexAttribIPointer(
|
||||||
|
index,
|
||||||
|
element.getTypeCount(),
|
||||||
|
element.getTypeGL(),
|
||||||
|
layout.stride(),
|
||||||
|
reinterpret_cast<const void*>(element.offset()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BufferElementType::Bool:
|
||||||
|
case BufferElementType::Bool2:
|
||||||
|
case BufferElementType::Bool3:
|
||||||
|
case BufferElementType::Bool4:
|
||||||
|
case BufferElementType::Float:
|
||||||
|
case BufferElementType::Vec2:
|
||||||
|
case BufferElementType::Vec3:
|
||||||
|
case BufferElementType::Vec4:
|
||||||
|
case BufferElementType::Mat2:
|
||||||
|
case BufferElementType::Mat3:
|
||||||
|
case BufferElementType::Mat4: {
|
||||||
glVertexAttribPointer(
|
glVertexAttribPointer(
|
||||||
index,
|
index,
|
||||||
element.getTypeCount(),
|
element.getTypeCount(),
|
||||||
element.getTypeGL(),
|
element.getTypeGL(),
|
||||||
element.getNormalized() ? GL_TRUE : GL_FALSE,
|
element.normalized() ? GL_TRUE : GL_FALSE,
|
||||||
layout.getStride(),
|
layout.stride(),
|
||||||
reinterpret_cast<const void*>(element.getOffset()));
|
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++;
|
index++;
|
||||||
}
|
}
|
||||||
m_vertexBuffers.push_back(std::move(vertexBuffer));
|
|
||||||
|
|
||||||
unbind();
|
unbind();
|
||||||
vertexBuffer->unbind();
|
vertexBuffer->unbind();
|
||||||
|
|
||||||
|
m_vertexBuffers.push_back(std::move(vertexBuffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexArray::setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer)
|
void VertexArray::setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer)
|
||||||
@@ -375,10 +436,10 @@ void VertexArray::setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer)
|
|||||||
bind();
|
bind();
|
||||||
indexBuffer->bind();
|
indexBuffer->bind();
|
||||||
|
|
||||||
m_indexBuffer = std::move(indexBuffer);
|
|
||||||
|
|
||||||
unbind();
|
unbind();
|
||||||
indexBuffer->unbind();
|
indexBuffer->unbind();
|
||||||
|
|
||||||
|
m_indexBuffer = std::move(indexBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Inferno
|
} // namespace Inferno
|
||||||
|
|||||||
+42
-37
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 Riyyi
|
* Copyright (C) 2022,2024 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
@@ -7,52 +7,54 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // int32_t, uint32_t
|
#include <cstdint> // int32_t, uint8_t, uint32_t
|
||||||
#include <memory> // std::shared_ptr
|
#include <memory> // std::shared_ptr
|
||||||
#include <string> // std::string
|
#include <string>
|
||||||
#include <vector> // std::vector
|
#include <vector>
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)
|
// https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)
|
||||||
enum class BufferElementType {
|
enum class BufferElementType : uint8_t {
|
||||||
None = 0,
|
None = 0,
|
||||||
Bool, Bool2, Bool3, Bool4, // bvec
|
Bool, Bool2, Bool3, Bool4, // bvec
|
||||||
Int, Int2, Int3, Int4, // ivec
|
Int, Int2, Int3, Int4, // ivec
|
||||||
Uint, Uint2, Uint3, Uint4, // uvec
|
Uint, Uint2, Uint3, Uint4, // uvec
|
||||||
Float, Vec2, Vec3, Vec4, // vec
|
Float, Vec2, Vec3, Vec4, // vec
|
||||||
Double, Double2, Double3, Double4, // dvec
|
Double, Vec2Double, Vec3Double, Vec4Double, // dvec
|
||||||
Mat2, Mat3, Mat4, // mat
|
Mat2, Mat3, Mat4, // mat
|
||||||
DoubleMat2, DoubleMat3, DoubleMat4, // dmat
|
MatDouble2, MatDouble3, MatDouble4, // dmat
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
// Describes one element of the BufferLayout
|
// Describes one element of the BufferLayout
|
||||||
class BufferElement {
|
class BufferElement final {
|
||||||
public:
|
public:
|
||||||
BufferElement(BufferElementType type, std::string name, bool normalized = false);
|
BufferElement(BufferElementType type, std::string name, bool normalized = false);
|
||||||
|
~BufferElement() = default;
|
||||||
|
|
||||||
uint32_t getTypeSize() const;
|
uint32_t getTypeSize() const;
|
||||||
uint32_t getTypeCount() const;
|
uint32_t getTypeCount() const;
|
||||||
uint32_t getTypeGL() const;
|
uint32_t getTypeGL() const;
|
||||||
static uint32_t getTypeSize(const BufferElementType type);
|
static uint32_t getTypeSize(BufferElementType type);
|
||||||
static uint32_t getTypeCount(const BufferElementType type);
|
static uint32_t getTypeCount(BufferElementType type);
|
||||||
static uint32_t getTypeGL(const BufferElementType type);
|
static uint32_t getTypeGL(BufferElementType type);
|
||||||
|
static uint32_t getGLTypeSize(uint32_t type);
|
||||||
|
|
||||||
inline BufferElementType getType() const { return m_type; }
|
BufferElementType type() const { return m_type; }
|
||||||
inline std::string getName() const { return m_name; }
|
std::string name() const { return m_name; }
|
||||||
inline uint32_t getSize() const { return m_size; }
|
uint32_t size() const { return m_size; }
|
||||||
inline uint32_t getOffset() const { return m_offset; }
|
uint32_t offset() const { return m_offset; }
|
||||||
inline bool getNormalized() const { return m_normalized; }
|
bool normalized() const { return m_normalized; }
|
||||||
|
|
||||||
inline void setType(const BufferElementType& type) { m_type = type; }
|
void setType(BufferElementType type) { m_type = type; }
|
||||||
inline void setName(const std::string& name) { m_name = name; }
|
void setName(const std::string& name) { m_name = name; }
|
||||||
inline void setSize(const uint32_t& size) { m_size = size; }
|
void setSize(uint32_t size) { m_size = size; }
|
||||||
inline void setOffset(const uint32_t& offset) { m_offset = offset; }
|
void setOffset(uint32_t offset) { m_offset = offset; }
|
||||||
inline void setNormalized(const bool& normalized) { m_normalized = normalized; }
|
void setNormalized(bool normalized) { m_normalized = normalized; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BufferElementType m_type;
|
BufferElementType m_type;
|
||||||
@@ -65,19 +67,20 @@ private:
|
|||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
// Layout that describes raw vertex data
|
// Layout that describes raw vertex data
|
||||||
class BufferLayout {
|
class BufferLayout final {
|
||||||
public:
|
public:
|
||||||
BufferLayout() {}
|
BufferLayout() {}
|
||||||
BufferLayout(const std::initializer_list<BufferElement>& elements);
|
BufferLayout(const std::initializer_list<BufferElement>& elements);
|
||||||
|
~BufferLayout() = default;
|
||||||
|
|
||||||
inline const std::vector<BufferElement>& getElements() const { return m_elements; }
|
const std::vector<BufferElement>& elements() const { return m_elements; }
|
||||||
inline uint32_t getStride() const { return m_stride; }
|
uint32_t stride() const { return m_stride; }
|
||||||
|
|
||||||
// Iterators
|
// Iterators
|
||||||
inline std::vector<BufferElement>::iterator begin() { return m_elements.begin(); }
|
std::vector<BufferElement>::iterator begin() { return m_elements.begin(); }
|
||||||
inline std::vector<BufferElement>::iterator end() { return m_elements.end(); }
|
std::vector<BufferElement>::iterator end() { return m_elements.end(); }
|
||||||
inline std::vector<BufferElement>::const_iterator begin() const { return m_elements.begin(); }
|
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>::const_iterator end() const { return m_elements.end(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void calculateOffsetsAndStride();
|
void calculateOffsetsAndStride();
|
||||||
@@ -90,10 +93,10 @@ private:
|
|||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
// GPU memory which holds raw vertex data
|
// GPU memory which holds raw vertex data
|
||||||
class VertexBuffer {
|
class VertexBuffer final { // Vertex Buffer Object, VBO
|
||||||
public:
|
public:
|
||||||
VertexBuffer(size_t size);
|
VertexBuffer(size_t size);
|
||||||
VertexBuffer(float* vertices, size_t size);
|
VertexBuffer(size_t size, float* vertices);
|
||||||
~VertexBuffer();
|
~VertexBuffer();
|
||||||
|
|
||||||
void bind() const;
|
void bind() const;
|
||||||
@@ -101,9 +104,9 @@ public:
|
|||||||
|
|
||||||
void uploadData(const void* data, uint32_t size);
|
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:
|
private:
|
||||||
uint32_t m_id { 0 };
|
uint32_t m_id { 0 };
|
||||||
@@ -113,7 +116,7 @@ private:
|
|||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
// Vertices order of rendering
|
// Vertices order of rendering
|
||||||
class IndexBuffer {
|
class IndexBuffer final { // Element Buffer Object, EBO
|
||||||
public:
|
public:
|
||||||
IndexBuffer(uint32_t* indices, size_t size);
|
IndexBuffer(uint32_t* indices, size_t size);
|
||||||
~IndexBuffer();
|
~IndexBuffer();
|
||||||
@@ -121,7 +124,9 @@ public:
|
|||||||
void bind() const;
|
void bind() const;
|
||||||
void unbind() 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:
|
private:
|
||||||
uint32_t m_id { 0 };
|
uint32_t m_id { 0 };
|
||||||
@@ -131,7 +136,7 @@ private:
|
|||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
// Array that holds the vertex attributes configuration
|
// Array that holds the vertex attributes configuration
|
||||||
class VertexArray {
|
class VertexArray final { // Vertex Array Object, VAO
|
||||||
public:
|
public:
|
||||||
VertexArray();
|
VertexArray();
|
||||||
~VertexArray();
|
~VertexArray();
|
||||||
@@ -142,8 +147,8 @@ public:
|
|||||||
void addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer);
|
void addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer);
|
||||||
void setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer);
|
void setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer);
|
||||||
|
|
||||||
inline const std::vector<std::shared_ptr<VertexBuffer>>& getVertexBuffers() const { return m_vertexBuffers; }
|
std::shared_ptr<VertexBuffer> at(size_t i) const { return m_vertexBuffers.at(i); }
|
||||||
inline std::shared_ptr<IndexBuffer> getIndexBuffer() const { return m_indexBuffer; }
|
std::shared_ptr<IndexBuffer> indexBuffer() const { return m_indexBuffer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t m_id { 0 };
|
uint32_t m_id { 0 };
|
||||||
|
|||||||
@@ -1,21 +1,159 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 Riyyi
|
* Copyright (C) 2022,2024 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* 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"
|
#include "inferno/render/framebuffer.h"
|
||||||
|
|
||||||
namespace Inferno {
|
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()
|
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
|
} // namespace Inferno
|
||||||
|
|||||||
@@ -1,17 +1,105 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 Riyyi
|
* Copyright (C) 2022,2024 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#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 {
|
namespace Inferno {
|
||||||
|
|
||||||
class Framebuffer {
|
class Framebuffer final { // Frame Buffer Object, FBO
|
||||||
public:
|
public:
|
||||||
Framebuffer();
|
enum Type : uint8_t {
|
||||||
virtual ~Framebuffer();
|
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
|
} // 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
|
||||||
+361
-331
@@ -4,228 +4,237 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm> // std::min
|
#include <algorithm> // std::copy, std::min
|
||||||
#include <utility> // std::move
|
#include <span>
|
||||||
|
|
||||||
#include "glad/glad.h"
|
#include "glad/glad.h"
|
||||||
|
#include "glm/ext/vector_float4.hpp" // glm::vec4
|
||||||
#include "ruc/format/log.h"
|
#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/buffer.h"
|
||||||
|
#include "inferno/render/render-command.h"
|
||||||
#include "inferno/render/renderer.h"
|
#include "inferno/render/renderer.h"
|
||||||
#include "inferno/render/shader.h"
|
|
||||||
#include "inferno/render/texture.h"
|
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
void RenderCommand::initialize()
|
template<typename T>
|
||||||
{
|
void Renderer<T>::beginScene(glm::mat4, glm::mat4)
|
||||||
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::clear()
|
template<typename T>
|
||||||
|
void Renderer<T>::endScene()
|
||||||
{
|
{
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
nextBatch();
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
uint32_t Renderer::m_supportedTextureUnitPerBatch = 0;
|
template<typename T>
|
||||||
|
void Renderer<T>::initialize()
|
||||||
void Renderer::initialize()
|
|
||||||
{
|
{
|
||||||
// Get amount of texture units supported by the GPU
|
// Get amount of texture units supported by the GPU
|
||||||
uint32_t constTextureUnitCount = textureUnitPerBatch;
|
|
||||||
uint32_t gpuTextureUnitCount = RenderCommand::textureUnitAmount();
|
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
|
// Texture unit 0 is reserved for no texture
|
||||||
m_textureUnits[0] = nullptr;
|
m_textureSlots[0] = nullptr;
|
||||||
|
|
||||||
// Create texture unit samplers
|
// Create texture unit samplers
|
||||||
int32_t samplers[textureUnitPerBatch];
|
int32_t samplers[maxTextureSlots];
|
||||||
for (uint32_t i = 0; i < textureUnitPerBatch; i++) {
|
for (uint32_t i = 0; i < maxTextureSlots; i++) {
|
||||||
samplers[i] = i;
|
samplers[i] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create shader
|
// Create shader
|
||||||
loadShader();
|
loadShader();
|
||||||
m_shader->bind();
|
m_shader->bind();
|
||||||
m_shader->setInt("u_textures", samplers, textureUnitPerBatch);
|
m_shader->setInt("u_textures", samplers, maxTextureSlots);
|
||||||
m_shader->unbind();
|
m_shader->unbind();
|
||||||
|
|
||||||
// Create vertex array
|
// Create vertex array
|
||||||
m_vertexArray = std::make_shared<VertexArray>();
|
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) {
|
if (texture == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new batch if the texture unit limit has been reached
|
// Create a new batch if the texture unit limit has been reached
|
||||||
if (m_textureUnitIndex >= m_supportedTextureUnitPerBatch) {
|
if (m_textureSlotIndex >= m_maxSupportedTextureSlots) {
|
||||||
nextBatch();
|
nextBatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If texure was already added
|
// If texure was already added
|
||||||
for (uint32_t i = 1; i < m_textureUnitIndex; i++) {
|
for (uint32_t i = 1; i < m_textureSlotIndex; i++) {
|
||||||
if (m_textureUnits[i] == texture) {
|
if (m_textureSlots[i] == texture) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add texture
|
// Add texture
|
||||||
uint32_t textureUnitIndex = m_textureUnitIndex;
|
uint32_t textureSlotIndex = m_textureSlotIndex;
|
||||||
m_textureUnits[textureUnitIndex] = texture;
|
m_textureSlots[textureSlotIndex] = texture;
|
||||||
m_textureUnitIndex++;
|
m_textureSlotIndex++;
|
||||||
|
|
||||||
return textureUnitIndex;
|
return textureSlotIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::bind()
|
template<typename T>
|
||||||
|
void Renderer<T>::bind()
|
||||||
{
|
{
|
||||||
m_shader->bind();
|
m_shader->bind();
|
||||||
|
|
||||||
for (uint32_t i = 1; i < m_textureUnitIndex; i++) {
|
for (uint32_t i = 1; i < m_textureSlotIndex; i++) {
|
||||||
m_textureUnits[i]->bind(i);
|
m_textureSlots[i]->bind(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_vertexArray->bind();
|
m_vertexArray->bind();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::unbind()
|
template<typename T>
|
||||||
|
void Renderer<T>::unbind()
|
||||||
{
|
{
|
||||||
m_vertexArray->unbind();
|
m_vertexArray->unbind();
|
||||||
|
|
||||||
for (uint32_t i = 1; i < m_textureUnitIndex; i++) {
|
for (uint32_t i = 1; i < m_textureSlotIndex; i++) {
|
||||||
m_textureUnits[i]->unbind();
|
m_textureSlots[i]->unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_shader->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)
|
Renderer2D::Renderer2D(s)
|
||||||
{
|
{
|
||||||
Renderer::initialize();
|
Renderer2D::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::~Renderer2D()
|
Renderer2D::~Renderer2D()
|
||||||
{
|
{
|
||||||
Renderer::destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer2D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
|
void Renderer2D::initialize()
|
||||||
{
|
{
|
||||||
m_shader->bind();
|
Renderer::initialize();
|
||||||
m_shader->setFloat("u_projectionView", cameraProjection * cameraView);
|
|
||||||
m_shader->unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer2D::endScene()
|
// ---------------------------------
|
||||||
{
|
// CPU
|
||||||
nextBatch();
|
|
||||||
|
// 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)
|
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)
|
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
|
// 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();
|
nextBatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,62 +273,39 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color,
|
|||||||
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
|
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
|
||||||
m_vertexBufferPtr->color = color[i];
|
m_vertexBufferPtr->color = color[i];
|
||||||
m_vertexBufferPtr->textureCoordinates = textureCoordinates[i];
|
m_vertexBufferPtr->textureCoordinates = textureCoordinates[i];
|
||||||
m_vertexBufferPtr->textureIndex = (float)textureUnitIndex;
|
m_vertexBufferPtr->textureIndex = textureUnitIndex;
|
||||||
m_vertexBufferPtr++;
|
m_vertexBufferPtr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_quadIndex++;
|
m_vertexIndex += vertexPerQuad;
|
||||||
|
m_elementIndex += elementPerQuad;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer2D::loadShader()
|
void Renderer2D::loadShader()
|
||||||
{
|
{
|
||||||
m_shader = ShaderManager::the().load("assets/glsl/batch-quad");
|
m_shader = AssetManager::the().load<Shader>("assets/glsl/batch-2d");
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
RendererCubemap::RendererCubemap(s)
|
RendererCubemap::RendererCubemap(s)
|
||||||
|
{
|
||||||
|
RendererCubemap::initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
RendererCubemap::~RendererCubemap()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RendererCubemap::initialize()
|
||||||
{
|
{
|
||||||
Renderer::initialize();
|
Renderer::initialize();
|
||||||
|
|
||||||
// CPU
|
|
||||||
// ---------------------------------
|
// ---------------------------------
|
||||||
|
// CPU
|
||||||
|
|
||||||
// Create array for storing quads vertices
|
// Create array for storing quads vertices
|
||||||
m_vertexBufferBase = std::make_unique<CubemapVertex[]>(vertexCount);
|
m_vertexBufferBase = std::make_unique<CubemapVertex[]>(maxVertices);
|
||||||
m_vertexBufferPtr = m_vertexBufferBase.get();
|
m_vertexBufferPtr = m_vertexBufferBase.get();
|
||||||
|
|
||||||
// Set default cubemap vertex positions
|
// Set default cubemap vertex positions
|
||||||
@@ -360,47 +346,23 @@ RendererCubemap::RendererCubemap(s)
|
|||||||
m_vertexPositions[22] = { 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 };
|
m_vertexPositions[23] = { 0.5f, -0.5f, -0.5f, 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
|
|
||||||
// ---------------------------------
|
// ---------------------------------
|
||||||
|
// GPU
|
||||||
|
|
||||||
|
m_enableDepthBuffer = false;
|
||||||
|
|
||||||
// Create vertex buffer
|
// Create vertex buffer
|
||||||
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(CubemapVertex) * vertexCount);
|
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(CubemapVertex) * maxVertices);
|
||||||
vertexBuffer->setLayout({
|
vertexBuffer->setLayout({
|
||||||
{ BufferElementType::Vec3, "a_position" },
|
{ BufferElementType::Vec3, "a_position" },
|
||||||
{ BufferElementType::Vec4, "a_color" },
|
{ BufferElementType::Vec4, "a_color" },
|
||||||
{ BufferElementType::Float, "a_textureIndex" },
|
{ BufferElementType::Uint, "a_textureIndex" },
|
||||||
});
|
});
|
||||||
m_vertexArray->addVertexBuffer(vertexBuffer);
|
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("RendererCubemap initialized");
|
ruc::info("RendererCubemap initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
RendererCubemap::~RendererCubemap()
|
|
||||||
{
|
|
||||||
Renderer::destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RendererCubemap::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
|
void RendererCubemap::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
|
||||||
{
|
{
|
||||||
// We want the skybox fixed in position, so only retain the rotation and scale.
|
// We want the skybox fixed in position, so only retain the rotation and scale.
|
||||||
@@ -416,11 +378,6 @@ void RendererCubemap::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraVie
|
|||||||
m_shader->unbind();
|
m_shader->unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererCubemap::endScene()
|
|
||||||
{
|
|
||||||
nextBatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture)
|
void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture)
|
||||||
{
|
{
|
||||||
drawCubemap(transform, glm::mat4(color, color, color, color), texture);
|
drawCubemap(transform, glm::mat4(color, color, color, color), texture);
|
||||||
@@ -429,103 +386,55 @@ void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::vec4
|
|||||||
void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> 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
|
// Create a new batch if the quad limit has been reached
|
||||||
if (m_quadIndex >= quadCount) {
|
if (m_vertexIndex + (vertexPerQuad * quadPerCube) > maxVertices
|
||||||
|
|| m_elementIndex + (elementPerQuad * quadPerCube) > maxElements) {
|
||||||
nextBatch();
|
nextBatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t textureUnitIndex = addTextureUnit(texture);
|
uint32_t textureUnitIndex = addTextureUnit(texture);
|
||||||
|
|
||||||
// Add the quads 4 vertices
|
// Add the quads 4 vertices, 6 times, once per cube side
|
||||||
for (uint32_t i = 0; i < vertexPerQuad * quadPerCube; i++) {
|
for (uint32_t i = 0; i < vertexPerQuad * quadPerCube; i++) {
|
||||||
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
|
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
|
||||||
m_vertexBufferPtr->color = color[i % 4];
|
m_vertexBufferPtr->color = color[i % 4];
|
||||||
m_vertexBufferPtr->textureIndex = (float)textureUnitIndex;
|
m_vertexBufferPtr->textureIndex = textureUnitIndex;
|
||||||
m_vertexBufferPtr++;
|
m_vertexBufferPtr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_quadIndex += quadPerCube;
|
m_vertexIndex += vertexPerQuad * quadPerCube;
|
||||||
|
m_elementIndex += elementPerQuad * quadPerCube;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererCubemap::loadShader()
|
void RendererCubemap::loadShader()
|
||||||
{
|
{
|
||||||
m_shader = ShaderManager::the().load("assets/glsl/batch-cubemap");
|
m_shader = AssetManager::the().load<Shader>("assets/glsl/batch-cubemap");
|
||||||
}
|
|
||||||
|
|
||||||
void RendererCubemap::flush()
|
|
||||||
{
|
|
||||||
if (m_quadIndex == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload vertex data to GPU
|
|
||||||
m_vertexArray->getVertexBuffers().at(0)->uploadData(
|
|
||||||
m_vertexBufferBase.get(),
|
|
||||||
m_quadIndex * vertexPerQuad * sizeof(CubemapVertex));
|
|
||||||
|
|
||||||
bind();
|
|
||||||
|
|
||||||
// Render
|
|
||||||
bool depthTest = RenderCommand::depthTest();
|
|
||||||
RenderCommand::setDepthTest(false);
|
|
||||||
RenderCommand::drawIndexed(*m_vertexArray, m_quadIndex * indexPerQuad);
|
|
||||||
RenderCommand::setDepthTest(depthTest);
|
|
||||||
|
|
||||||
unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RendererCubemap::startBatch()
|
|
||||||
{
|
|
||||||
m_quadIndex = 0;
|
|
||||||
m_vertexBufferPtr = m_vertexBufferBase.get();
|
|
||||||
|
|
||||||
m_textureUnitIndex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RendererCubemap::nextBatch()
|
|
||||||
{
|
|
||||||
flush();
|
|
||||||
startBatch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
RendererCharacter::RendererCharacter(s)
|
RendererFont::RendererFont(s)
|
||||||
{
|
{
|
||||||
Renderer::initialize();
|
Renderer::initialize();
|
||||||
|
|
||||||
// CPU
|
|
||||||
// ---------------------------------
|
// ---------------------------------
|
||||||
|
// CPU
|
||||||
|
|
||||||
// Create array for storing quads vertices
|
// Create array for storing quads vertices
|
||||||
m_vertexBufferBase = std::make_unique<CharacterVertex[]>(vertexCount);
|
m_vertexBufferBase = std::make_unique<SymbolVertex[]>(maxVertices);
|
||||||
m_vertexBufferPtr = m_vertexBufferBase.get();
|
m_vertexBufferPtr = m_vertexBufferBase.get();
|
||||||
|
|
||||||
// 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
|
|
||||||
// ---------------------------------
|
// ---------------------------------
|
||||||
|
// GPU
|
||||||
|
|
||||||
|
m_enableDepthBuffer = false;
|
||||||
|
|
||||||
// Create vertex buffer
|
// Create vertex buffer
|
||||||
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(CharacterVertex) * vertexCount);
|
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(SymbolVertex) * maxVertices);
|
||||||
vertexBuffer->setLayout({
|
vertexBuffer->setLayout({
|
||||||
{ BufferElementType::Vec3, "a_position" },
|
{ BufferElementType::Vec3, "a_position" },
|
||||||
{ BufferElementType::Vec4, "a_color" },
|
{ BufferElementType::Vec4, "a_color" },
|
||||||
{ BufferElementType::Vec2, "a_textureCoordinates" },
|
{ BufferElementType::Vec2, "a_textureCoordinates" },
|
||||||
{ BufferElementType::Float, "a_textureIndex" },
|
{ BufferElementType::Uint, "a_textureIndex" },
|
||||||
{ BufferElementType::Float, "a_width" },
|
{ BufferElementType::Float, "a_width" },
|
||||||
{ BufferElementType::Float, "a_edge" },
|
{ BufferElementType::Float, "a_edge" },
|
||||||
{ BufferElementType::Float, "a_borderWidth" },
|
{ BufferElementType::Float, "a_borderWidth" },
|
||||||
@@ -535,32 +444,17 @@ RendererCharacter::RendererCharacter(s)
|
|||||||
});
|
});
|
||||||
m_vertexArray->addVertexBuffer(vertexBuffer);
|
m_vertexArray->addVertexBuffer(vertexBuffer);
|
||||||
|
|
||||||
// Create index buffer
|
ruc::info("RendererFont initialized");
|
||||||
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * indexCount);
|
|
||||||
m_vertexArray->setIndexBuffer(indexBuffer);
|
|
||||||
delete[] indices;
|
|
||||||
|
|
||||||
ruc::info("RendererCharacter initialized");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RendererCharacter::~RendererCharacter()
|
RendererFont::~RendererFont()
|
||||||
{
|
|
||||||
Renderer::destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RendererCharacter::beginScene()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererCharacter::endScene()
|
void RendererFont::drawSymbol(std::array<SymbolVertex, vertexPerQuad>& symbolQuad, std::shared_ptr<Texture> texture)
|
||||||
{
|
|
||||||
nextBatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RendererCharacter::drawCharacter(std::array<CharacterVertex, vertexPerQuad>& characterQuad, std::shared_ptr<Texture> texture)
|
|
||||||
{
|
{
|
||||||
// Create a new batch if the quad limit has been reached
|
// 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();
|
nextBatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,63 +462,199 @@ void RendererCharacter::drawCharacter(std::array<CharacterVertex, vertexPerQuad>
|
|||||||
|
|
||||||
// Add the quads 4 vertices
|
// Add the quads 4 vertices
|
||||||
for (uint32_t i = 0; i < vertexPerQuad; i++) {
|
for (uint32_t i = 0; i < vertexPerQuad; i++) {
|
||||||
m_vertexBufferPtr->quad.position = characterQuad[i].quad.position;
|
m_vertexBufferPtr->quad.position = symbolQuad[i].quad.position;
|
||||||
m_vertexBufferPtr->quad.color = characterQuad[i].quad.color;
|
m_vertexBufferPtr->quad.color = symbolQuad[i].quad.color;
|
||||||
m_vertexBufferPtr->quad.textureCoordinates = characterQuad[i].quad.textureCoordinates;
|
m_vertexBufferPtr->quad.textureCoordinates = symbolQuad[i].quad.textureCoordinates;
|
||||||
m_vertexBufferPtr->quad.textureIndex = (float)textureUnitIndex;
|
m_vertexBufferPtr->quad.textureIndex = textureUnitIndex;
|
||||||
|
|
||||||
m_vertexBufferPtr->width = characterQuad[i].width;
|
m_vertexBufferPtr->width = symbolQuad[i].width;
|
||||||
m_vertexBufferPtr->edge = characterQuad[i].edge;
|
m_vertexBufferPtr->edge = symbolQuad[i].edge;
|
||||||
m_vertexBufferPtr->borderWidth = characterQuad[i].borderWidth;
|
m_vertexBufferPtr->borderWidth = symbolQuad[i].borderWidth;
|
||||||
m_vertexBufferPtr->borderEdge = characterQuad[i].borderEdge;
|
m_vertexBufferPtr->borderEdge = symbolQuad[i].borderEdge;
|
||||||
m_vertexBufferPtr->borderColor = characterQuad[i].borderColor;
|
m_vertexBufferPtr->borderColor = symbolQuad[i].borderColor;
|
||||||
m_vertexBufferPtr->offset = characterQuad[i].offset;
|
m_vertexBufferPtr->offset = symbolQuad[i].offset;
|
||||||
|
|
||||||
m_vertexBufferPtr++;
|
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) {
|
Renderer::initialize();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload vertex data to GPU
|
// ---------------------------------
|
||||||
m_vertexArray->getVertexBuffers().at(0)->uploadData(
|
// CPU
|
||||||
m_vertexBufferBase.get(),
|
|
||||||
m_quadIndex * vertexPerQuad * sizeof(CharacterVertex));
|
|
||||||
|
|
||||||
bind();
|
// Create array for storing quads vertices
|
||||||
|
m_vertexBufferBase = std::make_unique<Vertex[]>(maxVertices);
|
||||||
// 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;
|
|
||||||
m_vertexBufferPtr = m_vertexBufferBase.get();
|
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
|
} // namespace Inferno
|
||||||
|
|||||||
+149
-94
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <cstdint> // int32_t, uint32_t
|
#include <cstdint> // int32_t, uint32_t
|
||||||
#include <memory> // std::shared_ptr, std::unique_ptr, std::make_shared, std::make_unique
|
#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/matrix_float4x4.hpp" // glm::mat4
|
||||||
#include "glm/ext/vector_float2.hpp" // glm::vec2
|
#include "glm/ext/vector_float2.hpp" // glm::vec2
|
||||||
@@ -15,28 +16,28 @@
|
|||||||
#include "glm/ext/vector_float4.hpp" // glm::vec4
|
#include "glm/ext/vector_float4.hpp" // glm::vec4
|
||||||
#include "ruc/singleton.h"
|
#include "ruc/singleton.h"
|
||||||
|
|
||||||
#include "inferno/component/transformcomponent.h"
|
#include "inferno/asset/shader.h"
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
class Shader;
|
|
||||||
class Texture;
|
class Texture;
|
||||||
|
class TransformComponent;
|
||||||
class VertexArray;
|
class VertexArray;
|
||||||
|
|
||||||
struct QuadVertex {
|
struct QuadVertex {
|
||||||
glm::vec3 position { 0.0f, 0.0f, 0.0f };
|
glm::vec3 position { 0.0f };
|
||||||
glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f };
|
glm::vec4 color { 1.0f };
|
||||||
glm::vec2 textureCoordinates { 0.0f, 0.0f };
|
glm::vec2 textureCoordinates { 0.0f };
|
||||||
float textureIndex = 0; // TODO: get int to pass to fragment correctly
|
uint32_t textureIndex { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CubemapVertex {
|
struct CubemapVertex {
|
||||||
glm::vec3 position { 0.0f, 0.0f, 0.0f };
|
glm::vec3 position { 0.0f };
|
||||||
glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f };
|
glm::vec4 color { 1.0f };
|
||||||
float textureIndex = 0; // TODO: get int to pass to fragment correctly
|
uint32_t textureIndex { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CharacterVertex {
|
struct SymbolVertex {
|
||||||
QuadVertex quad;
|
QuadVertex quad;
|
||||||
|
|
||||||
// Font
|
// Font
|
||||||
@@ -45,40 +46,45 @@ struct CharacterVertex {
|
|||||||
// Outline
|
// Outline
|
||||||
float borderWidth = 0.7f;
|
float borderWidth = 0.7f;
|
||||||
float borderEdge = 0.1f;
|
float borderEdge = 0.1f;
|
||||||
glm::vec4 borderColor { 1.0f, 1.0f, 1.0f, 1.0f };
|
glm::vec4 borderColor { 1.0f };
|
||||||
// Dropshadow
|
// Dropshadow
|
||||||
float offset = 0.0f;
|
float offset = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------
|
struct Vertex {
|
||||||
|
glm::vec3 position { 0.0f };
|
||||||
class RenderCommand {
|
glm::vec3 normal { 1.0f };
|
||||||
public:
|
glm::vec4 color { 1.0f };
|
||||||
static void initialize();
|
glm::vec2 textureCoordinates { 0.0f };
|
||||||
static void destroy();
|
uint32_t textureIndex { 0 };
|
||||||
|
|
||||||
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();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
class Renderer {
|
class Renderer {
|
||||||
public:
|
public:
|
||||||
|
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 vertexPerQuad = 4;
|
||||||
static constexpr const uint32_t indexPerQuad = 6;
|
static constexpr const uint32_t elementPerQuad = 6;
|
||||||
static constexpr const uint32_t quadPerCube = 6;
|
|
||||||
static constexpr const uint32_t textureUnitPerBatch = 32;
|
// 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:
|
protected:
|
||||||
Renderer() {}
|
Renderer() {}
|
||||||
|
virtual ~Renderer() { destroy(); };
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
void destroy();
|
void destroy();
|
||||||
@@ -87,27 +93,43 @@ protected:
|
|||||||
|
|
||||||
void bind();
|
void bind();
|
||||||
void unbind();
|
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
|
// Texture units
|
||||||
static uint32_t m_supportedTextureUnitPerBatch;
|
static inline uint32_t m_maxSupportedTextureSlots { 0 };
|
||||||
uint32_t m_textureUnitIndex = 1;
|
uint32_t m_textureSlotIndex { 1 };
|
||||||
std::array<std::shared_ptr<Texture>, textureUnitPerBatch> m_textureUnits;
|
std::array<std::shared_ptr<Texture>, maxTextureSlots> m_textureSlots;
|
||||||
|
|
||||||
// GPU objects
|
// GPU objects
|
||||||
|
bool m_enableDepthBuffer { true };
|
||||||
|
uint32_t m_colorAttachmentCount { 1 };
|
||||||
std::shared_ptr<Shader> m_shader;
|
std::shared_ptr<Shader> m_shader;
|
||||||
std::shared_ptr<VertexArray> m_vertexArray;
|
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
|
class Renderer2D
|
||||||
: public Renderer
|
: public Renderer<QuadVertex>
|
||||||
, public ruc::Singleton<Renderer2D> {
|
, public ruc::Singleton<Renderer2D> {
|
||||||
public:
|
public:
|
||||||
Renderer2D(s);
|
Renderer2D(s);
|
||||||
@@ -115,64 +137,48 @@ public:
|
|||||||
|
|
||||||
using Singleton<Renderer2D>::destroy;
|
using Singleton<Renderer2D>::destroy;
|
||||||
|
|
||||||
// When to start a new batch
|
|
||||||
static constexpr const uint32_t quadCount = 1000;
|
|
||||||
static constexpr const uint32_t vertexCount = quadCount * vertexPerQuad;
|
|
||||||
static constexpr const uint32_t indexCount = quadCount * indexPerQuad;
|
|
||||||
|
|
||||||
void beginScene(glm::mat4 cameraProjectionView, glm::mat4 cameraView);
|
|
||||||
void endScene();
|
|
||||||
|
|
||||||
void drawQuad(const TransformComponent& transform, glm::vec4 color);
|
void drawQuad(const TransformComponent& transform, glm::vec4 color);
|
||||||
void drawQuad(const TransformComponent& transform, glm::mat4 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::vec4 color, std::shared_ptr<Texture> texture);
|
||||||
void drawQuad(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture);
|
void drawQuad(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture);
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
void loadShader() override;
|
Renderer2D() {} // Needed for derived classes
|
||||||
void flush() override;
|
|
||||||
void startBatch() override;
|
|
||||||
void nextBatch() override;
|
|
||||||
|
|
||||||
// CPU quad vertices
|
void initialize();
|
||||||
std::unique_ptr<QuadVertex[]> m_vertexBufferBase;
|
|
||||||
QuadVertex* m_vertexBufferPtr { nullptr };
|
|
||||||
|
|
||||||
// Default quad vertex positions
|
// Default quad vertex positions
|
||||||
glm::vec4 m_vertexPositions[vertexPerQuad];
|
glm::vec4 m_vertexPositions[vertexPerQuad];
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void loadShader() override;
|
||||||
};
|
};
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
|
|
||||||
class RendererCubemap final
|
class RendererCubemap
|
||||||
: public Renderer
|
: public Renderer<CubemapVertex>
|
||||||
, public ruc::Singleton<RendererCubemap> {
|
, public ruc::Singleton<RendererCubemap> {
|
||||||
|
public:
|
||||||
|
static constexpr const uint32_t quadPerCube = 6;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RendererCubemap(s);
|
RendererCubemap(s);
|
||||||
virtual ~RendererCubemap();
|
virtual ~RendererCubemap();
|
||||||
|
|
||||||
using Singleton<RendererCubemap>::destroy;
|
using Singleton<RendererCubemap>::destroy;
|
||||||
|
|
||||||
// When to start a new batch
|
virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) override;
|
||||||
static constexpr const uint32_t cubemapCount = 166;
|
|
||||||
static constexpr const uint32_t quadCount = cubemapCount * quadPerCube;
|
|
||||||
static constexpr const uint32_t vertexCount = quadCount * vertexPerQuad;
|
|
||||||
static constexpr const uint32_t indexCount = quadCount * indexPerQuad;
|
|
||||||
|
|
||||||
void beginScene(glm::mat4 cameraProjectionView, glm::mat4 cameraView);
|
|
||||||
void endScene();
|
|
||||||
|
|
||||||
void drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture);
|
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);
|
void drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture);
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
void loadShader() override;
|
RendererCubemap() {} // Needed for derived classes
|
||||||
void flush() override;
|
|
||||||
void startBatch() override;
|
|
||||||
void nextBatch() override;
|
|
||||||
|
|
||||||
// CPU quad vertices
|
void initialize();
|
||||||
std::unique_ptr<CubemapVertex[]> m_vertexBufferBase;
|
|
||||||
CubemapVertex* m_vertexBufferPtr { nullptr };
|
private:
|
||||||
|
virtual void loadShader() override;
|
||||||
|
|
||||||
// Default cubemap vertex positions
|
// Default cubemap vertex positions
|
||||||
glm::vec4 m_vertexPositions[vertexPerQuad * quadPerCube];
|
glm::vec4 m_vertexPositions[vertexPerQuad * quadPerCube];
|
||||||
@@ -180,33 +186,82 @@ private:
|
|||||||
|
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
|
|
||||||
class RendererCharacter final
|
class RendererFont final
|
||||||
: public Renderer
|
: public Renderer<SymbolVertex>
|
||||||
, public ruc::Singleton<RendererCharacter> {
|
, public ruc::Singleton<RendererFont> {
|
||||||
public:
|
public:
|
||||||
RendererCharacter(s);
|
RendererFont(s);
|
||||||
virtual ~RendererCharacter();
|
virtual ~RendererFont();
|
||||||
|
|
||||||
using Singleton<RendererCharacter>::destroy;
|
using Singleton<RendererFont>::destroy;
|
||||||
|
|
||||||
static const uint32_t quadCount = 1000;
|
void drawSymbol(std::array<SymbolVertex, vertexPerQuad>& symbolQuad, std::shared_ptr<Texture> texture);
|
||||||
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);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadShader() override;
|
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
|
} // 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,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022,2024 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 Texture2D;
|
|
||||||
class TextureCubemap;
|
|
||||||
|
|
||||||
class Texture {
|
|
||||||
public:
|
|
||||||
virtual ~Texture();
|
|
||||||
|
|
||||||
enum Type : uint8_t {
|
|
||||||
TwoDimensional = 0,
|
|
||||||
Cubemap,
|
|
||||||
};
|
|
||||||
|
|
||||||
void init(uint32_t width, uint32_t height, uint8_t channels);
|
|
||||||
|
|
||||||
virtual void bind(uint32_t unit = 0) const = 0;
|
|
||||||
virtual void unbind() const = 0;
|
|
||||||
|
|
||||||
std::string path() const { return m_path; }
|
|
||||||
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; }
|
|
||||||
|
|
||||||
virtual bool is2D() const { return false; }
|
|
||||||
virtual bool isCubeMap() const { return false; }
|
|
||||||
|
|
||||||
friend Texture2D;
|
|
||||||
friend TextureCubemap;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Texture() {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::string m_path;
|
|
||||||
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 };
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------------------------------
|
|
||||||
|
|
||||||
class Texture2D final : public Texture {
|
|
||||||
public:
|
|
||||||
virtual ~Texture2D() = default;
|
|
||||||
|
|
||||||
// Factory function
|
|
||||||
static std::shared_ptr<Texture> create(const std::string& path);
|
|
||||||
|
|
||||||
virtual void bind(uint32_t unit = 0) const override;
|
|
||||||
virtual void unbind() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Texture2D() {}
|
|
||||||
|
|
||||||
virtual bool is2D() const override { return true; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void create(unsigned char* data);
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------------------------------
|
|
||||||
|
|
||||||
class TextureCubemap final : public Texture {
|
|
||||||
public:
|
|
||||||
virtual ~TextureCubemap() = default;
|
|
||||||
|
|
||||||
// Factory function
|
|
||||||
static std::shared_ptr<Texture> create(const std::string& path);
|
|
||||||
|
|
||||||
virtual void bind(uint32_t unit = 0) const override;
|
|
||||||
virtual void unbind() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
TextureCubemap() {};
|
|
||||||
|
|
||||||
virtual bool isCubeMap() const override { return true; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void create();
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------------------------------
|
|
||||||
|
|
||||||
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, Texture::Type type = Texture::Type::TwoDimensional);
|
|
||||||
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)
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
#include <limits> // std::numeric_limits
|
#include <limits> // std::numeric_limits
|
||||||
#include <utility> // std::pair
|
#include <utility> // std::pair
|
||||||
|
|
||||||
#include "entt/entity/entity.hpp" // ent::entity
|
#include "entt/entity/fwd.hpp" // ent::entity
|
||||||
#include "ruc/file.h"
|
#include "ruc/file.h"
|
||||||
#include "ruc/format/log.h"
|
#include "ruc/format/log.h"
|
||||||
#include "ruc/json/json.h"
|
#include "ruc/json/json.h"
|
||||||
@@ -19,11 +19,14 @@
|
|||||||
#include "inferno/component/cubemap-component.h"
|
#include "inferno/component/cubemap-component.h"
|
||||||
#include "inferno/component/id-component.h"
|
#include "inferno/component/id-component.h"
|
||||||
#include "inferno/component/luascriptcomponent.h"
|
#include "inferno/component/luascriptcomponent.h"
|
||||||
|
#include "inferno/component/model-component.h"
|
||||||
#include "inferno/component/nativescriptcomponent.h"
|
#include "inferno/component/nativescriptcomponent.h"
|
||||||
#include "inferno/component/spritecomponent.h"
|
#include "inferno/component/spritecomponent.h"
|
||||||
#include "inferno/component/tagcomponent.h"
|
#include "inferno/component/tagcomponent.h"
|
||||||
#include "inferno/component/textareacomponent.h"
|
#include "inferno/component/textareacomponent.h"
|
||||||
#include "inferno/component/transformcomponent.h"
|
#include "inferno/component/transformcomponent.h"
|
||||||
|
#include "inferno/render/renderer.h"
|
||||||
|
#include "inferno/render/uniformbuffer.h"
|
||||||
#include "inferno/scene/scene.h"
|
#include "inferno/scene/scene.h"
|
||||||
#include "inferno/script/nativescript.h"
|
#include "inferno/script/nativescript.h"
|
||||||
#include "inferno/system/camerasystem.h"
|
#include "inferno/system/camerasystem.h"
|
||||||
@@ -83,7 +86,6 @@ void Scene::update(float deltaTime)
|
|||||||
void Scene::render()
|
void Scene::render()
|
||||||
{
|
{
|
||||||
RenderSystem::the().render();
|
RenderSystem::the().render();
|
||||||
TextAreaSystem::the().render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::destroy()
|
void Scene::destroy()
|
||||||
@@ -166,6 +168,10 @@ uint32_t Scene::loadEntity(ruc::Json components, uint32_t parentEntity)
|
|||||||
auto& text = addComponent<TextAreaComponent>(entity);
|
auto& text = addComponent<TextAreaComponent>(entity);
|
||||||
components.at("text").getTo(text);
|
components.at("text").getTo(text);
|
||||||
}
|
}
|
||||||
|
if (components.exists("model")) {
|
||||||
|
auto& text = addComponent<ModelComponent>(entity);
|
||||||
|
components.at("model").getTo(text);
|
||||||
|
}
|
||||||
if (components.exists("children")) {
|
if (components.exists("children")) {
|
||||||
VERIFY(components.at("children").type() == ruc::Json::Type::Array);
|
VERIFY(components.at("children").type() == ruc::Json::Type::Array);
|
||||||
const auto& children = components.at("children").asArray();
|
const auto& children = components.at("children").asArray();
|
||||||
|
|||||||
@@ -53,10 +53,23 @@ std::pair<glm::mat4, glm::mat4> CameraSystem::projectionView()
|
|||||||
}
|
}
|
||||||
|
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
|
|
||||||
return {};
|
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)
|
void CameraSystem::updateOrthographic(TransformComponent& transform, CameraComponent& camera)
|
||||||
{
|
{
|
||||||
// Update camera matrix
|
// Update camera matrix
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public:
|
|||||||
* @brief Return a pair from the camera component: { projection, view }
|
* @brief Return a pair from the camera component: { projection, view }
|
||||||
*/
|
*/
|
||||||
std::pair<glm::mat4, glm::mat4> projectionView();
|
std::pair<glm::mat4, glm::mat4> projectionView();
|
||||||
|
glm::vec3 translate();
|
||||||
|
|
||||||
void setRegistry(std::shared_ptr<entt::registry> registry) { m_registry = registry; };
|
void setRegistry(std::shared_ptr<entt::registry> registry) { m_registry = registry; };
|
||||||
|
|
||||||
|
|||||||
@@ -4,27 +4,195 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "glm/ext/matrix_transform.hpp" // glm::translate, glm::rotate, glm::scale, glm::radians
|
#include <cstdint> // int32_t
|
||||||
|
|
||||||
|
#include "glad/glad.h"
|
||||||
#include "ruc/format/log.h"
|
#include "ruc/format/log.h"
|
||||||
|
|
||||||
#include "inferno/component/cubemap-component.h"
|
#include "inferno/component/cubemap-component.h"
|
||||||
|
#include "inferno/component/model-component.h"
|
||||||
#include "inferno/component/spritecomponent.h"
|
#include "inferno/component/spritecomponent.h"
|
||||||
#include "inferno/component/transformcomponent.h"
|
#include "inferno/component/transformcomponent.h"
|
||||||
|
#include "inferno/render/framebuffer.h"
|
||||||
|
#include "inferno/render/render-command.h"
|
||||||
#include "inferno/render/renderer.h"
|
#include "inferno/render/renderer.h"
|
||||||
|
#include "inferno/render/shader-storage-buffer.h"
|
||||||
|
#include "inferno/render/shader-structs.h"
|
||||||
|
#include "inferno/render/uniformbuffer.h"
|
||||||
|
#include "inferno/system/camerasystem.h"
|
||||||
#include "inferno/system/rendersystem.h"
|
#include "inferno/system/rendersystem.h"
|
||||||
|
#include "inferno/system/textareasystem.h"
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
RenderSystem::RenderSystem(s)
|
RenderSystem::RenderSystem(s)
|
||||||
{
|
{
|
||||||
ruc::info("RenderSystem initialized");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderSystem::~RenderSystem()
|
RenderSystem::~RenderSystem()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void RenderSystem::initialize(uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
m_framebuffer = Framebuffer::create({
|
||||||
|
.attachments = { Framebuffer::Type::Color, Framebuffer::Type::RGBA16F, Framebuffer::Type::RGBA16F, Framebuffer::Type::Depth },
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
.clearColor = { 0.0f, 0.0f, 0.0f, 0.0f },
|
||||||
|
.clearBit = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
|
||||||
|
});
|
||||||
|
|
||||||
|
m_screenFramebuffer = Framebuffer::create({
|
||||||
|
.renderToScreen = true,
|
||||||
|
.clearColor = { 1.0f, 1.0f, 1.0f, 1.0f },
|
||||||
|
.clearBit = GL_COLOR_BUFFER_BIT,
|
||||||
|
});
|
||||||
|
|
||||||
|
Uniformbuffer::the().setLayout(
|
||||||
|
"Camera", 0,
|
||||||
|
{
|
||||||
|
{ BufferElementType::Mat4, "u_projectionView" },
|
||||||
|
{ BufferElementType::Vec3, "u_position" },
|
||||||
|
});
|
||||||
|
Uniformbuffer::the().create("Camera");
|
||||||
|
|
||||||
|
ShaderStorageBuffer::the().setLayout("DirectionalLights", 0, RendererPostProcess::the().shaderID());
|
||||||
|
ShaderStorageBuffer::the().create("DirectionalLights");
|
||||||
|
|
||||||
|
ruc::info("RenderSystem initialized");
|
||||||
|
}
|
||||||
|
|
||||||
void RenderSystem::render()
|
void RenderSystem::render()
|
||||||
|
{
|
||||||
|
static constexpr TransformComponent transformIdentity;
|
||||||
|
|
||||||
|
// ---------------------------------
|
||||||
|
// Deferred rendering to a framebuffer
|
||||||
|
|
||||||
|
framebufferSetup(m_framebuffer);
|
||||||
|
|
||||||
|
renderGeometry();
|
||||||
|
|
||||||
|
framebufferTeardown(m_framebuffer);
|
||||||
|
|
||||||
|
// ---------------------------------
|
||||||
|
// Forward rendering to the screen
|
||||||
|
|
||||||
|
framebufferSetup(m_screenFramebuffer);
|
||||||
|
|
||||||
|
renderSkybox();
|
||||||
|
|
||||||
|
// Render 3D geometry post-processing
|
||||||
|
RendererPostProcess::the().drawQuad(transformIdentity, m_framebuffer->texture(0), m_framebuffer->texture(1), m_framebuffer->texture(2));
|
||||||
|
RendererPostProcess::the().endScene();
|
||||||
|
|
||||||
|
// Visual representation of light sources
|
||||||
|
Framebuffer::copyBuffer(m_framebuffer, m_screenFramebuffer, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
||||||
|
renderLightCubes();
|
||||||
|
|
||||||
|
// Render 2D, UI
|
||||||
|
renderOverlay();
|
||||||
|
|
||||||
|
framebufferTeardown(m_screenFramebuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderSystem::resize(int32_t width, int32_t height)
|
||||||
|
{
|
||||||
|
RenderCommand::setViewport(0, 0, width, height);
|
||||||
|
m_framebuffer->resize(width, height);
|
||||||
|
m_screenFramebuffer->resize(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void RenderSystem::framebufferSetup(std::shared_ptr<Framebuffer> framebuffer)
|
||||||
|
{
|
||||||
|
framebuffer->bind();
|
||||||
|
|
||||||
|
RenderCommand::setColorAttachmentCount(framebuffer->colorAttachmentCount());
|
||||||
|
RenderCommand::clearColor(framebuffer->clearColor());
|
||||||
|
RenderCommand::clearBit(framebuffer->clearBit());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderSystem::framebufferTeardown(std::shared_ptr<Framebuffer> framebuffer)
|
||||||
|
{
|
||||||
|
framebuffer->unbind();
|
||||||
|
|
||||||
|
RenderCommand::setColorAttachmentCount(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderSystem::renderGeometry()
|
||||||
|
{
|
||||||
|
auto [projection, view] = CameraSystem::the().projectionView();
|
||||||
|
auto translate = CameraSystem::the().translate();
|
||||||
|
Uniformbuffer::the().setValue("Camera", "u_projectionView", projection * view);
|
||||||
|
Uniformbuffer::the().setValue("Camera", "u_position", translate);
|
||||||
|
|
||||||
|
static DirectionalLightBlock directionalLights[2] = {
|
||||||
|
{
|
||||||
|
.direction = { -8.0f, -8.0f, -8.0f },
|
||||||
|
.ambient = { 0.1f, 0.1f, 0.1f },
|
||||||
|
.diffuse = { 1.0f, 1.0f, 1.0f },
|
||||||
|
.specular = { 1.0f, 1.0f, 1.0f },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.direction = { 8.0f, 8.0f, 8.0f },
|
||||||
|
.ambient = { 0.1f, 0.1f, 0.1f },
|
||||||
|
.diffuse = { 1.0f, 1.0f, 1.0f },
|
||||||
|
.specular = { 1.0f, 0.0f, 0.0f },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ShaderStorageBuffer::the().setValue("DirectionalLights", "u_directionalLight", directionalLights);
|
||||||
|
|
||||||
|
auto modelView = m_registry->view<TransformComponent, ModelComponent>();
|
||||||
|
|
||||||
|
for (auto [entity, transform, model] : modelView.each()) {
|
||||||
|
Renderer3D::the().drawModel(model.model->vertices(),
|
||||||
|
model.model->elements(),
|
||||||
|
transform,
|
||||||
|
model.color,
|
||||||
|
model.model->texture() ? model.model->texture() : model.texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
Renderer3D::the().endScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderSystem::renderSkybox()
|
||||||
|
{
|
||||||
|
auto [projection, view] = CameraSystem::the().projectionView();
|
||||||
|
RendererCubemap::the().beginScene(projection, view); // camera, lights, environment
|
||||||
|
|
||||||
|
auto cubemapView = m_registry->view<TransformComponent, CubemapComponent>();
|
||||||
|
|
||||||
|
for (auto [entity, transform, cubemap] : cubemapView.each()) {
|
||||||
|
if (!cubemap.isLight) {
|
||||||
|
RendererCubemap::the().drawCubemap(transform, cubemap.color, cubemap.texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RendererCubemap::the().endScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderSystem::renderLightCubes()
|
||||||
|
{
|
||||||
|
auto [projection, view] = CameraSystem::the().projectionView();
|
||||||
|
RendererLightCube::the().beginScene(projection, view); // camera, lights, environment
|
||||||
|
|
||||||
|
auto cubemapView = m_registry->view<TransformComponent, CubemapComponent>();
|
||||||
|
|
||||||
|
for (auto [entity, transform, cubemap] : cubemapView.each()) {
|
||||||
|
if (cubemap.isLight) {
|
||||||
|
RendererLightCube::the().drawCubemap(transform, cubemap.color, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RendererLightCube::the().endScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderSystem::renderOverlay()
|
||||||
{
|
{
|
||||||
auto quadView = m_registry->view<TransformComponent, SpriteComponent>();
|
auto quadView = m_registry->view<TransformComponent, SpriteComponent>();
|
||||||
|
|
||||||
@@ -32,11 +200,10 @@ void RenderSystem::render()
|
|||||||
Renderer2D::the().drawQuad(transform, sprite.color, sprite.texture);
|
Renderer2D::the().drawQuad(transform, sprite.color, sprite.texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cubemapView = m_registry->view<TransformComponent, CubemapComponent>();
|
Renderer2D::the().endScene();
|
||||||
|
|
||||||
for (auto [entity, transform, cubemap] : cubemapView.each()) {
|
TextAreaSystem::the().render();
|
||||||
RendererCubemap::the().drawCubemap(transform, cubemap.color, cubemap.texture);
|
RendererFont::the().endScene();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Inferno
|
} // namespace Inferno
|
||||||
|
|||||||
@@ -6,25 +6,39 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint> // int32_t, uint32_t
|
||||||
#include <memory> //std::shared_ptr
|
#include <memory> //std::shared_ptr
|
||||||
|
|
||||||
#include "entt/entity/registry.hpp" // entt::entity, entt::registry
|
#include "entt/entity/fwd.hpp" // entt::registry
|
||||||
|
|
||||||
#include "ruc/singleton.h"
|
#include "ruc/singleton.h"
|
||||||
|
|
||||||
#include "inferno/render/renderer.h"
|
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
|
class Framebuffer;
|
||||||
|
|
||||||
class RenderSystem final : public ruc::Singleton<RenderSystem> {
|
class RenderSystem final : public ruc::Singleton<RenderSystem> {
|
||||||
public:
|
public:
|
||||||
RenderSystem(s);
|
RenderSystem(s);
|
||||||
virtual ~RenderSystem();
|
virtual ~RenderSystem();
|
||||||
|
|
||||||
|
void initialize(uint32_t width, uint32_t height);
|
||||||
void render();
|
void render();
|
||||||
|
|
||||||
|
void resize(int32_t width, int32_t height);
|
||||||
|
|
||||||
void setRegistry(std::shared_ptr<entt::registry> registry) { m_registry = registry; };
|
void setRegistry(std::shared_ptr<entt::registry> registry) { m_registry = registry; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void framebufferSetup(std::shared_ptr<Framebuffer> framebuffer);
|
||||||
|
void framebufferTeardown(std::shared_ptr<Framebuffer> framebuffer);
|
||||||
|
void renderGeometry();
|
||||||
|
void renderSkybox();
|
||||||
|
void renderLightCubes();
|
||||||
|
void renderOverlay();
|
||||||
|
|
||||||
|
std::shared_ptr<Framebuffer> m_framebuffer;
|
||||||
|
std::shared_ptr<Framebuffer> m_screenFramebuffer;
|
||||||
std::shared_ptr<entt::registry> m_registry;
|
std::shared_ptr<entt::registry> m_registry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 Riyyi
|
* Copyright (C) 2022-2024 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm> // std::max
|
|
||||||
|
|
||||||
#include "ruc/format/log.h"
|
#include "ruc/format/log.h"
|
||||||
#include "ruc/meta/assert.h"
|
#include "ruc/meta/assert.h"
|
||||||
|
|
||||||
#include "inferno/application.h"
|
#include "inferno/application.h"
|
||||||
|
#include "inferno/asset/font.h"
|
||||||
|
#include "inferno/asset/texture.h"
|
||||||
#include "inferno/component/textareacomponent.h"
|
#include "inferno/component/textareacomponent.h"
|
||||||
#include "inferno/render/font.h"
|
#include "inferno/component/transformcomponent.h"
|
||||||
#include "inferno/render/renderer.h"
|
#include "inferno/render/renderer.h"
|
||||||
#include "inferno/render/texture.h"
|
|
||||||
#include "inferno/scene/scene.h"
|
#include "inferno/scene/scene.h"
|
||||||
#include "inferno/system/textareasystem.h"
|
#include "inferno/system/textareasystem.h"
|
||||||
#include "inferno/window.h"
|
#include "inferno/window.h"
|
||||||
@@ -42,19 +41,19 @@ void TextAreaSystem::render()
|
|||||||
// Loop through textareas content
|
// Loop through textareas content
|
||||||
// Linebreak if width reached
|
// Linebreak if width reached
|
||||||
// Break if lines AND width reached
|
// Break if lines AND width reached
|
||||||
// Calculate character quad
|
// Calculate symbol quad
|
||||||
// Submit character quad for rendering
|
// Submit symbol quad for rendering
|
||||||
|
|
||||||
std::shared_ptr<Font> font = FontManager::the().load(textarea.font);
|
std::shared_ptr<Font> font = AssetManager::the().load<Font>(textarea.font);
|
||||||
// glm::mat4 translate = transform.translate;
|
// glm::mat4 translate = transform.translate;
|
||||||
|
|
||||||
m_characters.clear();
|
m_symbols.clear();
|
||||||
createLines(font, textarea);
|
createLines(font, textarea);
|
||||||
createQuads(font, textarea);
|
createQuads(font, textarea);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using Characters = std::vector<std::shared_ptr<Character>>;
|
using Symbols = std::vector<std::shared_ptr<Symbol>>;
|
||||||
|
|
||||||
void TextAreaSystem::createLines(std::shared_ptr<Font> font, const TextAreaComponent& textarea)
|
void TextAreaSystem::createLines(std::shared_ptr<Font> font, const TextAreaComponent& textarea)
|
||||||
{
|
{
|
||||||
@@ -73,9 +72,9 @@ void TextAreaSystem::createLines(std::shared_ptr<Font> font, const TextAreaCompo
|
|||||||
size_t spaceIndex = 0;
|
size_t spaceIndex = 0;
|
||||||
float lineWidth = 0.0f;
|
float lineWidth = 0.0f;
|
||||||
float lineWidthSinceLastSpace = 0.0f;
|
float lineWidthSinceLastSpace = 0.0f;
|
||||||
for (char character : textarea.content) {
|
for (char symbol : textarea.content) {
|
||||||
auto c = font->get(character);
|
auto c = font->get(symbol);
|
||||||
m_characters.push_back(c);
|
m_symbols.push_back(c);
|
||||||
|
|
||||||
// Kerning
|
// Kerning
|
||||||
char kerning = 0;
|
char kerning = 0;
|
||||||
@@ -86,18 +85,18 @@ void TextAreaSystem::createLines(std::shared_ptr<Font> font, const TextAreaCompo
|
|||||||
lineWidth += (c->advance + kerning) * fontScale;
|
lineWidth += (c->advance + kerning) * fontScale;
|
||||||
lineWidthSinceLastSpace += (c->advance + kerning) * fontScale;
|
lineWidthSinceLastSpace += (c->advance + kerning) * fontScale;
|
||||||
|
|
||||||
if (character == ' ') {
|
if (symbol == ' ') {
|
||||||
spaceIndex = m_characters.size() - 1;
|
spaceIndex = m_symbols.size() - 1;
|
||||||
lineWidthSinceLastSpace = 0;
|
lineWidthSinceLastSpace = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lineWidth > textureWidth) {
|
if (lineWidth > textureWidth) {
|
||||||
m_characters[spaceIndex] = nullptr;
|
m_symbols[spaceIndex] = nullptr;
|
||||||
lineWidth = 0;
|
lineWidth = 0;
|
||||||
lineWidth = lineWidthSinceLastSpace;
|
lineWidth = lineWidthSinceLastSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
previous = character;
|
previous = symbol;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,26 +107,26 @@ void TextAreaSystem::createQuads(std::shared_ptr<Font> font, const TextAreaCompo
|
|||||||
char previous = 0;
|
char previous = 0;
|
||||||
float advanceX = 0.0f;
|
float advanceX = 0.0f;
|
||||||
float advanceY = 0.0f;
|
float advanceY = 0.0f;
|
||||||
for (const auto& character : m_characters) {
|
for (const auto& symbol : m_symbols) {
|
||||||
// Go to the next line on "\n"
|
// Go to the next line on "\n"
|
||||||
if (character == nullptr) {
|
if (symbol == nullptr) {
|
||||||
advanceX = 0;
|
advanceX = 0;
|
||||||
advanceY -= (font->lineSpacing() * textarea.lineSpacing) * fontScale;
|
advanceY -= (font->lineSpacing() * textarea.lineSpacing) * fontScale;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<CharacterQuad> quad = calculateCharacterQuad(character, previous, font, fontScale, advanceX, advanceY);
|
std::optional<SymbolQuad> quad = calculateSymbolQuad(symbol, previous, font, fontScale, advanceX, advanceY);
|
||||||
if (quad) {
|
if (quad) {
|
||||||
RendererCharacter::the().drawCharacter(quad.value(), font->texture());
|
RendererFont::the().drawSymbol(quad.value(), font->texture());
|
||||||
}
|
}
|
||||||
|
|
||||||
previous = character->id;
|
previous = symbol->id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(std::shared_ptr<Character> c, char previous, std::shared_ptr<Font> font, float fontScale, float& advanceX, float& advanceY)
|
std::optional<SymbolQuad> TextAreaSystem::calculateSymbolQuad(std::shared_ptr<Symbol> c, char previous, std::shared_ptr<Font> font, float fontScale, float& advanceX, float& advanceY)
|
||||||
{
|
{
|
||||||
CharacterQuad characterQuad;
|
SymbolQuad symbolQuad;
|
||||||
|
|
||||||
// Texture
|
// Texture
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
@@ -136,7 +135,7 @@ std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(std::shared_
|
|||||||
float textureHeight = static_cast<float>(font->texture()->height());
|
float textureHeight = static_cast<float>(font->texture()->height());
|
||||||
VERIFY(textureWidth == textureHeight, "TextAreaSystem read invalid font texture");
|
VERIFY(textureWidth == textureHeight, "TextAreaSystem read invalid font texture");
|
||||||
|
|
||||||
// Skip empty characters (like space)
|
// Skip empty symbols (like space)
|
||||||
if (c->size.x == 0 || c->size.y == 0) {
|
if (c->size.x == 0 || c->size.y == 0) {
|
||||||
// Jump to the next glyph
|
// Jump to the next glyph
|
||||||
advanceX += c->advance * fontScale;
|
advanceX += c->advance * fontScale;
|
||||||
@@ -167,10 +166,10 @@ std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(std::shared_
|
|||||||
(cursorMax.y / textureHeight * 2) + 1,
|
(cursorMax.y / textureHeight * 2) + 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
characterQuad.at(0).quad.position = { cursorScreen.x, cursorScreenMax.y, 0.0f }; // bottom left
|
symbolQuad.at(0).quad.position = { cursorScreen.x, cursorScreenMax.y, 0.0f }; // bottom left
|
||||||
characterQuad.at(1).quad.position = { cursorScreenMax.x, cursorScreenMax.y, 0.0f }; // bottom right
|
symbolQuad.at(1).quad.position = { cursorScreenMax.x, cursorScreenMax.y, 0.0f }; // bottom right
|
||||||
characterQuad.at(2).quad.position = { cursorScreenMax.x, cursorScreen.y, 0.0f }; // top right
|
symbolQuad.at(2).quad.position = { cursorScreenMax.x, cursorScreen.y, 0.0f }; // top right
|
||||||
characterQuad.at(3).quad.position = { cursorScreen.x, cursorScreen.y, 0.0f }; // top left
|
symbolQuad.at(3).quad.position = { cursorScreen.x, cursorScreen.y, 0.0f }; // top left
|
||||||
|
|
||||||
// Jump to the next glyph
|
// Jump to the next glyph
|
||||||
advanceX += (c->advance + kerning) * fontScale;
|
advanceX += (c->advance + kerning) * fontScale;
|
||||||
@@ -187,12 +186,12 @@ std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(std::shared_
|
|||||||
(textureHeight - c->position.y) / textureHeight
|
(textureHeight - c->position.y) / textureHeight
|
||||||
};
|
};
|
||||||
|
|
||||||
characterQuad.at(0).quad.textureCoordinates = { x.x, y.x };
|
symbolQuad.at(0).quad.textureCoordinates = { x.x, y.x };
|
||||||
characterQuad.at(1).quad.textureCoordinates = { x.y, y.x };
|
symbolQuad.at(1).quad.textureCoordinates = { x.y, y.x };
|
||||||
characterQuad.at(2).quad.textureCoordinates = { x.y, y.y };
|
symbolQuad.at(2).quad.textureCoordinates = { x.y, y.y };
|
||||||
characterQuad.at(3).quad.textureCoordinates = { x.x, y.y };
|
symbolQuad.at(3).quad.textureCoordinates = { x.x, y.y };
|
||||||
|
|
||||||
return characterQuad;
|
return symbolQuad;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Inferno
|
} // namespace Inferno
|
||||||
|
|||||||
@@ -1,30 +1,27 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2022 Riyyi
|
* Copyright (C) 2022-2024 Riyyi
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint> // std::uint32_t
|
|
||||||
#include <memory> // std::shared_ptr
|
#include <memory> // std::shared_ptr
|
||||||
#include <optional> // std::optional
|
#include <optional> // std::optional
|
||||||
#include <vector> // std::vector
|
#include <vector> // std::vector
|
||||||
|
|
||||||
#include "glm/ext/vector_float3.hpp" // glm::vec3
|
|
||||||
#include "ruc/singleton.h"
|
#include "ruc/singleton.h"
|
||||||
|
|
||||||
#include "inferno/component/textareacomponent.h"
|
#include "inferno/asset/font.h"
|
||||||
#include "inferno/render/font.h"
|
|
||||||
#include "inferno/render/renderer.h"
|
#include "inferno/render/renderer.h"
|
||||||
|
|
||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
using Characters = std::vector<std::shared_ptr<Character>>;
|
using Symbols = std::vector<std::shared_ptr<Symbol>>;
|
||||||
using CharacterQuad = std::array<CharacterVertex, Renderer::vertexPerQuad>;
|
using SymbolQuad = std::array<SymbolVertex, RendererFont::vertexPerQuad>;
|
||||||
|
|
||||||
class Font;
|
|
||||||
class Scene;
|
class Scene;
|
||||||
|
class TextAreaComponent;
|
||||||
|
|
||||||
class TextAreaSystem final : public ruc::Singleton<TextAreaSystem> {
|
class TextAreaSystem final : public ruc::Singleton<TextAreaSystem> {
|
||||||
public:
|
public:
|
||||||
@@ -39,9 +36,9 @@ private:
|
|||||||
void createLines(std::shared_ptr<Font> font, const TextAreaComponent& textarea);
|
void createLines(std::shared_ptr<Font> font, const TextAreaComponent& textarea);
|
||||||
void createQuads(std::shared_ptr<Font> font, const TextAreaComponent& textarea);
|
void createQuads(std::shared_ptr<Font> font, const TextAreaComponent& textarea);
|
||||||
|
|
||||||
std::optional<CharacterQuad> calculateCharacterQuad(std::shared_ptr<Character> c, char previous, std::shared_ptr<Font> font, float fontSize, float& advanceX, float& advanceY);
|
std::optional<SymbolQuad> calculateSymbolQuad(std::shared_ptr<Symbol> c, char previous, std::shared_ptr<Font> font, float fontSize, float& advanceX, float& advanceY);
|
||||||
|
|
||||||
Characters m_characters;
|
Symbols m_symbols;
|
||||||
Scene* m_scene { nullptr };
|
Scene* m_scene { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "inferno/io/input.h"
|
#include "inferno/io/input.h"
|
||||||
#include "inferno/keycodes.h"
|
#include "inferno/keycodes.h"
|
||||||
#include "inferno/render/context.h"
|
#include "inferno/render/context.h"
|
||||||
|
#include "inferno/render/render-command.h"
|
||||||
#include "inferno/render/renderer.h"
|
#include "inferno/render/renderer.h"
|
||||||
#include "inferno/settings.h"
|
#include "inferno/settings.h"
|
||||||
#include "inferno/window.h"
|
#include "inferno/window.h"
|
||||||
@@ -121,17 +122,17 @@ void Window::initialize()
|
|||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case GLFW_PRESS: {
|
case GLFW_PRESS: {
|
||||||
KeyPressEvent event(key);
|
KeyPressEvent event(key, mods);
|
||||||
w.m_eventCallback(event);
|
w.m_eventCallback(event);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GLFW_RELEASE: {
|
case GLFW_RELEASE: {
|
||||||
KeyReleaseEvent event(key);
|
KeyReleaseEvent event(key, mods);
|
||||||
w.m_eventCallback(event);
|
w.m_eventCallback(event);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GLFW_REPEAT: {
|
case GLFW_REPEAT: {
|
||||||
KeyRepeatEvent event(key);
|
KeyRepeatEvent event(key, mods);
|
||||||
w.m_eventCallback(event);
|
w.m_eventCallback(event);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+13
-1
@@ -9,6 +9,16 @@ add_subdirectory(glfw)
|
|||||||
# Add RUC target to project
|
# Add RUC target to project
|
||||||
add_subdirectory(ruc)
|
add_subdirectory(ruc)
|
||||||
|
|
||||||
|
# assimp options
|
||||||
|
option(ASSIMP_BUILD_ASSIMP_VIEW OFF)
|
||||||
|
option(ASSIMP_BUILD_TESTS OFF)
|
||||||
|
option(ASSIMP_INSTALL OFF)
|
||||||
|
option(ASSIMP_INSTALL_PDB OFF)
|
||||||
|
option(ASSIMP_WARNINGS_AS_ERRORS OFF)
|
||||||
|
|
||||||
|
# Add assimp target to project
|
||||||
|
add_subdirectory(assimp)
|
||||||
|
|
||||||
# Define engine source files
|
# Define engine source files
|
||||||
file(GLOB_RECURSE GLAD "glad/*.c")
|
file(GLOB_RECURSE GLAD "glad/*.c")
|
||||||
file(GLOB LUA "lua/lua/*.c")
|
file(GLOB LUA "lua/lua/*.c")
|
||||||
@@ -19,6 +29,8 @@ set(ENGINE_SOURCES ${GLAD} ${LUA})
|
|||||||
|
|
||||||
add_library(${ENGINE}-dependencies ${ENGINE_SOURCES})
|
add_library(${ENGINE}-dependencies ${ENGINE_SOURCES})
|
||||||
target_include_directories(${ENGINE}-dependencies PUBLIC
|
target_include_directories(${ENGINE}-dependencies PUBLIC
|
||||||
|
"assimp/include"
|
||||||
"glad/include"
|
"glad/include"
|
||||||
"lua")
|
"lua")
|
||||||
target_link_libraries(${ENGINE}-dependencies glfw ruc ruc-test)
|
target_link_libraries(${ENGINE}-dependencies glfw ruc ruc-test assimp)
|
||||||
|
target_compile_options(${ENGINE}-dependencies PRIVATE ${COMPILE_FLAGS_DEPS})
|
||||||
|
|||||||
+1
Submodule vendor/assimp added at 762ad8e9b7
Vendored
+63
@@ -0,0 +1,63 @@
|
|||||||
|
The glad source code:
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2022 David Herberth
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
The Khronos Specifications:
|
||||||
|
|
||||||
|
Copyright (c) 2013-2020 The Khronos Group Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
The EGL Specification and various headers:
|
||||||
|
|
||||||
|
Copyright (c) 2007-2016 The Khronos Group Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and/or associated documentation files (the
|
||||||
|
"Materials"), to deal in the Materials without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Materials, and to
|
||||||
|
permit persons to whom the Materials are furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Materials.
|
||||||
|
|
||||||
|
THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||||
Vendored
+1
-1
Submodule vendor/ruc updated: 07c9f9959d...d5bf50715e
Reference in New Issue
Block a user