Compare commits

...
18 Commits
Author SHA1 Message Date
Riyyi cc33b5d481 Render+Asset: Fix derived Renderer2D, fix Skybox uniform 2024-08-31 19:54:23 +02:00
Riyyi 67bb6a5671 Render: Fix release build via out-of-line destructors 2024-08-31 00:29:03 +02:00
Riyyi 3ff8838e01 Render: Fix memory leak, use after free :^) 2024-08-30 23:02:28 +02:00
Riyyi 3ae6ecae8a Meta: Enable ASan and UBSan, disable warnings in vendor directory
(address sanitizer, undefined behavior sanitizer)
2024-08-29 22:15:13 +02:00
Riyyi 6b6c588378 Component: Put JSON serialization functions in a separate file 2024-08-25 21:10:03 +02:00
Riyyi 9145f90c1d Meta: Update ruc library 2024-08-25 13:26:04 +02:00
Riyyi b9043dbb4b Render: Add and implement shader storage buffer (SSBO) 2024-08-24 20:14:06 +02:00
Riyyi b6e68eccec Doc: Add GLSL std430 memory layout documentation 2024-08-20 21:31:01 +02:00
Riyyi d16fade497 Everywhere: Work towards lighting support 2024-08-19 17:56:57 +02:00
Riyyi b8e4d9ef3f Render: Allow arbitrary data in uniformbuffer objects 2024-08-19 17:41:31 +02:00
Riyyi 03e8210165 Event+Asset+Keycodes: Add keybind for taking screenshots 2024-08-17 21:05:43 +02:00
Riyyi dcf2a85208 Asset+Component+Render: Render quads in the world as 3D objects 2024-08-11 22:06:55 +02:00
Riyyi b2ed951b1e Render: Add more value setter types for uniformbuffer objects 2024-08-11 22:06:55 +02:00
Riyyi d63dfe2f9f Render: Update getter to match naming convention 2024-08-10 22:05:21 +02:00
Riyyi faacdca424 App: Print average frame-time on exit 2024-08-10 21:58:50 +02:00
Riyyi b5f3cae5ba Render+Scene: Implement uniformbuffer objects 2024-08-10 21:58:50 +02:00
Riyyi 1d5f5a1ad8 Shader: Cache uniform location lookup 2024-08-09 23:10:39 +02:00
Riyyi 3cd2fab637 Render: Add support for int/double shader attributes 2024-08-07 21:52:13 +02:00
65 changed files with 2220 additions and 421 deletions
+22 -10
View File
@@ -11,6 +11,7 @@ endif()
# Options
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
option(INFERNO_BUILD_EXAMPLES "Build the Inferno example programs" ${INFERNO_STANDALONE})
option(INFERNO_BUILD_WARNINGS "Build with warnings enabled" ${INFERNO_STANDALONE})
# ------------------------------------------
@@ -25,10 +26,15 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Compiler flags used for all build types
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
# -Wall = All warnings about contructions that are easily avoidable
# -Wextra = Extra warning flags not covered by -Wall
# -Wpedantic = Warnings for compiler extensions not part of the standard
set(COMPILE_FLAGS_DEPS -w)
if(INFERNO_BUILD_WARNINGS)
set(COMPILE_FLAGS_PROJECT -Wall -Wextra -Wpedantic)
# -Wall = All warnings about contructions that are easily avoidable
# -Wextra = Extra warning flags not covered by -Wall
# -Wpedantic = Warnings for compiler extensions not part of the standard
else()
set(COMPILE_FLAGS_PROJECT ${COMPILE_FLAGS_DEPS})
endif()
# Set default build type if not specified
set(DEFAULT_BUILD_TYPE Release)
@@ -42,14 +48,20 @@ endif()
# Set build type specific compiler flags
message("--- ${CMAKE_BUILD_TYPE} ---")
if(${CMAKE_BUILD_TYPE} STREQUAL Debug)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -g -pg")
# -Og = Optimizations that do not interfere with debugging
# -g = Produce debugging information in OS's native format
# -pg = Generate profile information for analysis with gprof
# Optimizations that do not interfere with debugging
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -Og")
# Produce debugging information in OS's native format
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -g")
# Generate profile information for analysis with gprof
# $ gprof <PROJECT> gmon.out > profile-data.txt
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -pg")
# Enable ASan (Address Sanitizer) and UBSan (Undefined Behavior Sanitizer)
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fsanitize=address,undefined")
# Do not omit frame pointer, which helps with debugging.
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit-frame-pointer")
elseif(${CMAKE_BUILD_TYPE} STREQUAL Release)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
# -O3 = Optimizations that increase compilation time and performance
# Optimizations that increase compilation time and performance
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -O3")
endif()
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
@@ -4,14 +4,14 @@ layout(location = 0) out vec4 color;
in vec4 v_color;
in vec2 v_textureCoordinates;
in flat float v_textureIndex;
in flat uint v_textureIndex;
uniform sampler2D u_textures[32];
void main()
{
vec4 textureColor = v_color;
switch(int(v_textureIndex)) {
switch(v_textureIndex) {
case 0: break; // Texture unit 0 is reserved for no texture
case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
case 2: textureColor *= texture(u_textures[2], v_textureCoordinates); break;
+19
View File
@@ -0,0 +1,19 @@
#version 450 core
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in uint a_textureIndex;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat uint v_textureIndex;
void main()
{
v_color = a_color;
v_textureCoordinates = a_textureCoordinates;
v_textureIndex = a_textureIndex;
// Vclip = Model transform * Vlocal
gl_Position = vec4(a_position, 1.0f);
}
+13 -5
View File
@@ -1,17 +1,21 @@
#version 450 core
layout(location = 0) out vec4 color;
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 float v_textureIndex;
in flat uint v_textureIndex;
uniform sampler2D u_textures[32];
void main()
{
vec4 textureColor = vec4(1.0f);
switch(int(v_textureIndex)) {
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;
@@ -45,5 +49,9 @@ void main()
case 30: textureColor *= texture(u_textures[30], v_textureCoordinates); break;
case 31: textureColor *= texture(u_textures[31], v_textureCoordinates); break;
}
color = textureColor;
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);
}
+13 -4
View File
@@ -2,18 +2,27 @@
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_normal;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in float a_textureIndex;
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 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()
{
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
+2 -2
View File
@@ -4,14 +4,14 @@ layout(location = 0) out vec4 color;
in vec4 v_color;
in vec3 v_textureCoordinates;
in flat float v_textureIndex;
in flat uint v_textureIndex;
uniform samplerCube u_textures[32];
void main()
{
vec4 textureColor = v_color;
switch(int(v_textureIndex)) {
switch(v_textureIndex) {
case 0: break; // Texture unit 0 is reserved for no texture
case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
case 2: textureColor *= texture(u_textures[2], v_textureCoordinates); break;
+2 -2
View File
@@ -2,11 +2,11 @@
layout(location = 0) in vec3 a_position;
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 vec3 v_textureCoordinates;
out flat float v_textureIndex;
out flat uint v_textureIndex;
uniform mat4 u_projectionView;
+2 -2
View File
@@ -4,7 +4,7 @@ layout(location = 0) out vec4 color;
in vec4 v_color;
in vec2 v_textureCoordinates;
in flat float v_textureIndex;
in flat uint v_textureIndex;
in float v_width;
in float v_edge;
in float v_borderWidth;
@@ -25,7 +25,7 @@ float alpha(float textureAlpha)
void main()
{
vec4 textureColor = v_color;
switch(int(v_textureIndex)) {
switch(v_textureIndex) {
case 0: break; // Texture unit 0 is reserved for no texture
// case 1: textureColor.a = 1; break;
case 1: textureColor.a = alpha(texture(u_textures[1], v_textureCoordinates).a); break;
+2 -2
View File
@@ -3,7 +3,7 @@
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in float a_textureIndex;
layout(location = 3) in uint a_textureIndex;
layout(location = 4) in float a_width;
layout(location = 5) in float a_edge;
layout(location = 6) in float a_borderWidth;
@@ -13,7 +13,7 @@ layout(location = 9) in float a_offset;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat float v_textureIndex;
out flat uint v_textureIndex;
out float v_width;
out float v_edge;
out float v_borderWidth;
+17
View File
@@ -0,0 +1,17 @@
#version 450 core
layout(location = 0) out vec4 color;
in vec4 v_color;
in vec3 v_textureCoordinates;
in flat uint v_textureIndex;
uniform samplerCube u_textures[32];
void main()
{
// Prevent u_textures variable from getting optimized away
if (texture(u_textures[0], vec3(0.0f)).x > 99999.0f) { discard; }
color = v_color;
}
@@ -2,19 +2,22 @@
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in float a_textureIndex;
layout(location = 2) in uint a_textureIndex;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat float v_textureIndex;
out vec3 v_textureCoordinates;
out flat uint v_textureIndex;
uniform mat4 u_projectionView;
layout(std140, binding = 0) uniform Camera
{
mat4 u_projectionView;
vec3 u_position;
};
void main()
{
v_color = a_color;
v_textureCoordinates = a_textureCoordinates;
v_textureCoordinates = a_position;
v_textureIndex = a_textureIndex;
// Vclip = Camera projection * Camera view * Model transform * Vlocal
gl_Position = u_projectionView * vec4(a_position, 1.0f);
+78
View File
@@ -0,0 +1,78 @@
#version 450 core
layout(location = 0) out vec4 color;
in vec4 v_color;
in vec2 v_textureCoordinates;
in flat uint v_textureIndex;
uniform sampler2D u_textures[32];
// -----------------------------------------
layout(std140, binding = 0) uniform Camera {
mat4 u_projectionView;
vec3 u_position;
};
// -----------------------------------------
struct DirectionalLight {
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
const int MAX_DIRECTIONAL_LIGHTS = 4;
layout(std430, binding = 0) buffer DirectionalLights {
DirectionalLight u_directionalLight[MAX_DIRECTIONAL_LIGHTS];
};
// -----------------------------------------
void main()
{
float isObject = texture(u_textures[v_textureIndex + 1], v_textureCoordinates).a;
if (isObject == 0.0f) {
color = vec4(0,0,0,0);
return;
}
vec3 albedo = texture(u_textures[v_textureIndex + 0], v_textureCoordinates).rgb;
float specular = texture(u_textures[v_textureIndex + 0], v_textureCoordinates).a;
vec3 position = texture(u_textures[v_textureIndex + 1], v_textureCoordinates).rgb;
vec3 normal = texture(u_textures[v_textureIndex + 2], v_textureCoordinates).rgb;
vec3 lighting = vec3(0.0f, 0.0f, 0.0f);//albedo * v_color.xyz;
vec3 viewDirection = normalize(u_position - position);
// Loop through all directional lights
for (int i = 0; i < MAX_DIRECTIONAL_LIGHTS; ++i) {
// Diffuse
vec3 lightDirection = normalize(-u_directionalLight[i].direction);
float diffuse = max(dot(normal, lightDirection), 0.0f);
// Specular
vec3 reflectionDirection = reflect(-lightDirection, normal);
float specular = pow(max(dot(viewDirection, reflectionDirection), 0.0f), 32);
lighting +=
(albedo * u_directionalLight[i].ambient) +
(albedo * diffuse * u_directionalLight[i].diffuse) +
(specular * u_directionalLight[i].specular);
}
// Loop through all point lights
// TODO
// vec3 lightDirection = normalize(lightPosition - position);
// float diffuse = max(dot(normal, lightDirection), 0.0f);
// lighting += diffuse * albedo * u_lightColor;
// Loop through all spot lights
// TODO
color = vec4(lighting, 1.0f);
}
+25
View File
@@ -0,0 +1,25 @@
#version 450 core
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_textureCoordinates;
layout(location = 3) in uint a_textureIndex;
out vec4 v_color;
out vec2 v_textureCoordinates;
out flat uint v_textureIndex;
layout(std140, binding = 0) uniform Camera
{
mat4 u_projectionView;
vec3 u_position;
};
void main()
{
v_color = a_color;
v_textureCoordinates = a_textureCoordinates;
v_textureIndex = a_textureIndex;
// Vclip = Model transform * Vlocal
gl_Position = vec4(a_position, 1.0f);
}
Binary file not shown.
+15
View File
@@ -0,0 +1,15 @@
# Blender 4.2.0
# www.blender.org
usemtl (null)
o Plane
v -1.000000 -1.000000 -0.000000
v 1.000000 -1.000000 -0.000000
v -1.000000 1.000000 0.000000
v 1.000000 1.000000 0.000000
vn -0.0000 -0.0000 1.0000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
s 0
f 1/1/1 2/2/1 4/3/1 3/4/1
+10 -5
View File
@@ -35,8 +35,9 @@
"rotate": [ 0.0, 0.0, 0.0 ],
"scale": [ 1.0, 1.0, 1.0 ]
},
"sprite": {
"model": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test.png"
}
},
@@ -48,8 +49,9 @@
"rotate": [ 0.0, 0.0, 0.0 ],
"scale": [ 1.0, 1.0, 1.0 ]
},
"sprite": {
"model": {
"color": [ 0.5, 0.6, 0.8, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test.png"
}
},
@@ -61,8 +63,9 @@
"rotate": [ 0.0, 0.0, -20.0 ],
"scale": [ 1.0, 1.0, 1.0 ]
},
"sprite": {
"model": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test-inverted.png"
},
"children": [
@@ -74,8 +77,9 @@
"rotate": [ 0.0, 0.0, 0.0 ],
"scale": [ 0.5, 0.5, 1.0 ]
},
"sprite": {
"model": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test-inverted.png"
},
"children": [
@@ -87,8 +91,9 @@
"rotate": [ 0.0, 0.0, -20.0 ],
"scale": [ 0.5, 0.5, 1.0 ]
},
"sprite": {
"model": {
"color": [ 1.0, 1.0, 1.0, 1.0 ],
"model": "assets/model/quad.obj",
"texture": "assets/gfx/test-inverted.png"
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
"fullscreen": "windowed",
"height": 720,
"title": "Inferno",
"vsync": true,
"vsync": false,
"width": 1280
}
}
+9
View File
@@ -0,0 +1,9 @@
#+TITLE: Documentation
#+AUTHOR: Riyyi
#+LANGUAGE: en
#+OPTIONS: toc:nil
Topics:
- [[./shaders.org][Shaders]]
- [[./references.org][References]]
+326
View File
@@ -0,0 +1,326 @@
#+TITLE: Memory Layout
#+AUTHOR: Riyyi
#+LANGUAGE: en
#+OPTIONS: toc:nil
This chapter is about the memory layout of interface blocks in GLSL.
An interface block is a group of variables, a struct if you will.
There are 4 types of memory layouts that can be used, where the first two aren't
widely used as those are implementation dependent and require querying the
OpenGL API for memory offsets.
- packed
- shared
- std140
- std430
** std140
This type is usable in Uniform Buffer Objects (=UBO=) and Shader Storage Buffer
Objects (=SSBO=).
** std430
This type is only usable in Shader Storage Buffer Objects (=SSBO=).
Main points:
- Memory is organized into chunks.
- One chunk has 4 slots, 4 bytes per slot.
- Can't fit? Move to next chunk.
- An interface block is at least the size of 1 chunk.
The rules:
- Scalar =bool=, =int=, =uint=, =float=, and =double=
#+BEGIN_SRC
Both the size and alignment are the size of the scalar in basic machine types
(e.g., sizeof(GLfloat))
#+END_SRC
- Two-componment Vectors (e.g., =ivec2=)
#+BEGIN_SRC
Both the size and alignment are twice the size of the underlying scalar type.
#+END_SRC
- Three-component Vectors (e.g., =vec3=) and Four-component Vectors (e.g., =vec4=)
#+BEGIN_SRC
Both the size and alignment are four times the size of the scalar type. However,
this is only true when the member is part of an array or nested structure
#+END_SRC
- Array of Scalars and Vectors
#+BEGIN_SRC
The size of each element in the array will be the same size of the element type,
where three-component vectors are rounded up to the size four-component
vectors. This is also the array's alignment. The array's size will be the
element size times the number of elements.
#+END_SRC
- Column-major matrix or an array of column-major matrices of size C columns and R rows
#+BEGIN_SRC
Same layout as an array of N vectors each with R components, where N is the
total number of columns present.
#+END_SRC
- Row-major matrix or an array of row-major matrices of size R rows and C columns
#+BEGIN_SRC
Same layout as an array of N vectors each with C components, where N is the
total number of rows present.
#+END_SRC
Both =GLSL= and the =GLM= math library we're using have column-major matrices!
- Single-structure definition or an array of structures
#+BEGIN_SRC
Structure alignment is the same as the alignment of the biggest structure
member, where three-component vectors are rounded up to the size of
four-component vectors. Each structure will start on this alignment, and its
size will be the space neeeded by its members, according to the previous rules,
rounded up to a multiple of the structure alignment.
#+END_SRC
All of the examples described in the following segments are verified by querying
the OpenGL API.
*** Scalars
These types take up 1 (or 2 with =double=) slot and can appear after anything.
In the example below you can see that due to the larger alignment, the ~double~
is forced to the next chunk.
#+BEGIN_SRC glsl
// Var Size Alignment Offset
bool a; // 4 4 0
int b; // 4 4 4
uint c; // 4 4 8
double d; // 8 8 16
float e; // 4 4 24
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][b][c][ ] #1
[d][d][e][ ] #2
#+END_SRC
*** Two-component Vectors
**** Float
A Vec2 takes up 2 slots, so will be in the first or last half of a chunk.
#+BEGIN_SRC glsl
// Var Size Alignment Offset
vec2 a; // 8 8 0
float b; // 4 4 8
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][a][b][ ] #1
#+END_SRC
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float a; // 4 4 0
vec2 b; // 8 8 8
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][b][b] #1
#+END_SRC
**** Double
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float a; // 4 4 0
dvec2 b; // 16 16 16
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][ ][ ] #1
[b][b][b][b] #2
#+END_SRC
*** Three-component and Four-component Vectors
A Vec3 takes up 3 slots, the alignment is 4 slots so only fits at the start of a
chunk.
#+BEGIN_SRC glsl
// Var Size Alignment Offset
vec3 a; // 12 16 0
float b; // 4 4 12
vec4 c; // 16 16 16
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][a][a][b] #1
[c][c][c][c] #2
#+END_SRC
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float a; // 4 4 0
vec3 b; // 12 16 16
vec4 c; // 16 16 32
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][ ][ ] #1
[b][b][b][ ] #2
[c][c][c][c] #3
#+END_SRC
*** Array of Scalars and Vectors
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float[3] a; // 12 4 0
float b; // 4 4 12
float c; // 4 4 16
float[3] d; // 12 4 20
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][a][a][b] #1
[c][d][d][d] #1
#+END_SRC
*Note* the optimizations in the alignment and strides are not applicable to
~vec3~ elements, these remain unchanged from =std140=.
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float a; // 4 4 0
vec3[3] b; // 48 16 16
float c; // 4 4 64
float d; // 4 4 68
vec2[2] e; // 16 8 72
float f; // 4 4 88
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][ ][ ] #1, offset: 0
[b][b][b][ ] #2, offset: 16
[b][b][b][ ] #3, offset: 32
[b][b][b][ ] #4, offset: 48
[c][d][e][e] #5, offset: 64
[e][e][f][ ] #6, offset: 80
#+END_SRC
*Note* the offset needs to be a multiple of the alignment, forcing an entire
empty chunk in the example below.
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float a; // 4 4 0
dvec2[2] b; // 32 16 16
dvec3[2] c; // 64 32 64
float d; // 4 4 128
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][ ][ ] #1, offset: 0
[d][d][d][d] #2, offset: 16
[d][d][d][d] #3, offset: 32
[ ][ ][ ][ ] #4, offset: 48
[c][c][c][c] #5, offset: 64
[c][c][ ][ ] #6, offset: 80
[c][c][c][c] #7, offset: 96
[c][c][ ][ ] #8, offset: 112
[d][ ][ ][ ] #9, offset: 128
#+END_SRC
*** Matrices
Alignment is the same as an array of 1 “row” of the matrix.
No padding between the “rows” of a matrix, but will pad at the end.
#+BEGIN_SRC glsl
// Var Size Alignment Offset
float a; // 4 4 0
mat2 b; // 16 8 8
vec2 c; // 4 4 24
float d; // 4 4 32
mat2[2] e; // 32 8 40
float f; // 4 4 72
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][b][b] #1, offset: 0
[b][b][c][c] #2, offset: 16
[d][ ][e][e] #3, offset: 32
[e][e][e][e] #4, offset: 48
[e][e][f][ ] #5, offset: 64
#+END_SRC
TODO: Add more examples
*** Structs
Alignment same as biggest struct member. Size is the size of all members,
rounded up to a multiple of the alignment.
In the example below you can see that the ~Stuff~ struct, including padding
between members, is 20 bytes in size. To make that a multiple of the alignment
additional padding needs to be put at the end, to make the total size 24 bytes.
Each element in the array of structs will apply the alignment again, as seen
with ~Stuff[1].a~.
#+BEGIN_SRC glsl
struct Stuff {
float a;
vec2 b;
float c;
};
// Var Size Alignment Offset
Stuff a; // 20 8
a.a; // 4 4 0
a.b; // 8 8 8
a.c; // 4 4 16
float b; // 4 4 24
Stuff[2] c; // 44 8
c.a; // 4 4 32
c.b; // 8 8 40
c.c; // 4 4 48
float d; // 4 4 80
#+END_SRC
#+BEGIN_SRC
Chunks:
[a][ ][a][a] #1, offset: 0
[a][ ][b][ ] #2, offset: 16
[c][ ][c][c] #3, offset: 32
[c][ ][c][ ] #4, offset: 48
[c][c][c][ ] #5, offset: 64
[d][ ][ ][ ] #6, offset: 80
#+END_SRC
TODO: Add more examples
* References
- https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL
- https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout
- [[https://www.oreilly.com/library/view/opengl-programming-guide/9780132748445/app09lev1sec2.html][The std140 Layout Rules]]
- [[https://www.youtube.com/watch?v=JPvbRko9lBg][(YouTube) WebGL 2: Uniform Buffer Objects]]
+1
View File
@@ -0,0 +1 @@
+8
View File
@@ -0,0 +1,8 @@
#+TITLE: Shaders
#+AUTHOR: Riyyi
#+LANGUAGE: en
#+OPTIONS: toc:nil
Topics:
- [[./memory-layout.org][Memory Layout]]
+1
View File
@@ -15,6 +15,7 @@ add_executable(${GAME} ${GAME_SOURCES})
target_include_directories(${GAME} PRIVATE
"src")
target_link_libraries(${GAME} ${ENGINE})
target_compile_options(${GAME} PRIVATE ${COMPILE_FLAGS_PROJECT})
target_precompile_headers(${GAME} REUSE_FROM ${ENGINE})
+1
View File
@@ -10,6 +10,7 @@ target_include_directories(${ENGINE} PUBLIC
"../vendor/sol2/include"
"../vendor/stb")
target_link_libraries(${ENGINE} ${ENGINE}-dependencies)
target_compile_options(${ENGINE} PRIVATE ${COMPILE_FLAGS_PROJECT})
# ------------------------------------------
+19 -57
View File
@@ -23,6 +23,8 @@
#include "inferno/render/buffer.h"
#include "inferno/render/context.h"
#include "inferno/render/framebuffer.h"
#include "inferno/render/uniformbuffer.h"
#include "inferno/system/rendersystem.h"
// #include "inferno/render/gltf.h"
#include "inferno/asset/shader.h"
#include "inferno/asset/texture.h"
@@ -52,20 +54,7 @@ Application::Application()
Input::initialize();
RenderCommand::initialize();
m_framebuffer = Framebuffer::create({
.attachments = { Framebuffer::Type::Color, Framebuffer::Type::Depth },
.width = m_window->getWidth(),
.height = m_window->getHeight(),
.clearColor = { 0.2f, 0.3f, 0.3f, 1.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,
});
RenderSystem::the().initialize(m_window->getWidth(), m_window->getHeight());
m_scene = std::make_shared<Scene>();
m_scene->initialize();
@@ -90,11 +79,14 @@ Application::Application()
Application::~Application()
{
m_scene->destroy();
Uniformbuffer::destroy();
RendererFont::destroy();
Renderer2D::destroy();
Renderer3D::destroy();
RendererCubemap::destroy();
RendererPostProcess::destroy();
RendererLightCube::destroy();
RenderCommand::destroy();
AssetManager::destroy();
// Input::destroy();
@@ -158,11 +150,9 @@ int Application::run()
// offset
#endif
constexpr glm::vec4 vectorOne { 1.0f, 1.0f, 1.0f, 1.0f };
constexpr glm::mat4 matIdentity { 1.0f };
constexpr TransformComponent transformIdentity;
double gametime = 0;
uint64_t frames = 0;
// m_window->setVSync(false);
while (!m_window->shouldClose()) {
float time = Time::time();
@@ -170,6 +160,9 @@ int Application::run()
m_lastFrameTime = time;
// ruc::debug("Frametime {}ms", deltaTime * 1000);
gametime += deltaTime;
frames++;
// ---------------------------------
// Update
@@ -182,50 +175,15 @@ int Application::run()
// ---------------------------------
// Render
m_framebuffer->bind();
RenderCommand::clearColor(m_framebuffer->clearColor());
RenderCommand::clearBit(m_framebuffer->clearBit());
render();
std::pair<glm::mat4, glm::mat4> projectionView = m_scene->cameraProjectionView();
RendererCubemap::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
Renderer3D::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
Renderer2D::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
RendererFont::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
m_scene->render();
// RendererCharacter::the().drawCharacter(character, f->texture());
RendererCubemap::the().endScene();
Renderer3D::the().endScene();
Renderer2D::the().endScene();
RendererFont::the().endScene();
m_framebuffer->unbind();
// ---------------------------------
// Framebuffer
m_screenFramebuffer->bind();
RenderCommand::clearColor(m_screenFramebuffer->clearColor());
RenderCommand::clearBit(m_screenFramebuffer->clearBit());
Renderer2D::the().setEnableDepthBuffer(false);
Renderer2D::the().beginScene(matIdentity, matIdentity);
Renderer2D::the().drawQuad(transformIdentity, vectorOne, m_framebuffer->texture(0));
Renderer2D::the().endScene();
Renderer2D::the().setEnableDepthBuffer(true);
m_screenFramebuffer->unbind();
m_window->render();
}
ruc::debug("Application shutdown");
ruc::debug("Average frametime: {:.2f}ms", (gametime / frames) * 1000);
return m_status;
}
@@ -254,8 +212,7 @@ bool Application::onWindowResize(WindowResizeEvent& e)
{
ruc::info("WindowResizeEvent {}x{}", e.getWidth(), e.getHeight());
RenderCommand::setViewport(0, 0, e.getWidth(), e.getHeight());
m_framebuffer->resize(e.getWidth(), e.getHeight());
RenderSystem::the().resize(e.getWidth(), e.getHeight());
return true;
}
@@ -269,6 +226,11 @@ bool Application::onKeyPress(KeyPressEvent& e)
m_window->setShouldClose(true);
}
if (e.getKey() == keyCode("GLFW_KEY_F12")) {
ruc::info("Taking screenshot..");
Texture::saveScreenshotPNG("screenshot.png", m_window->getWidth(), m_window->getHeight());
}
return true;
}
-2
View File
@@ -51,8 +51,6 @@ private:
float m_lastFrameTime { 0.0f };
std::unique_ptr<Window> m_window;
std::shared_ptr<Framebuffer> m_framebuffer;
std::shared_ptr<Framebuffer> m_screenFramebuffer;
std::shared_ptr<Scene> m_scene;
//
+1 -1
View File
@@ -40,7 +40,7 @@ std::shared_ptr<Model> Model::create(std::string_view path)
void Model::processScene(std::shared_ptr<Model> model, const aiScene* scene)
{
VERIFY(scene->HasMeshes(), "malformed model");
VERIFY(scene->mNumTextures < 2, "unsupported model type");
VERIFY(scene->mNumTextures < 2, "unsupported model type: {}/1", scene->mNumTextures);
if (scene->mNumTextures == 1) {
aiTexture* texture = scene->mTextures[0];
+28 -17
View File
@@ -50,65 +50,74 @@ Shader::~Shader()
}
}
int32_t Shader::findUniform(std::string_view name) const
// -----------------------------------------
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(findUniform(name), value);
glUniform1i(findUniformLocation(name), value);
}
void Shader::setInt(std::string_view name, int* values, uint32_t count)
{
// Set uniform int array
glUniform1iv(findUniform(name), count, values);
glUniform1iv(findUniformLocation(name), count, values);
}
void Shader::setFloat(std::string_view name, float value) const
void Shader::setFloat(std::string_view name, float value)
{
// Set uniform float
glUniform1f(findUniform(name), value);
glUniform1f(findUniformLocation(name), value);
}
void Shader::setFloat(std::string_view name, float v1, float v2, float v3, float v4) const
void Shader::setFloat(std::string_view name, float v1, float v2, float v3, float v4)
{
// Set uniform vec4 data
glUniform4f(findUniform(name), v1, v2, v3, v4);
glUniform4f(findUniformLocation(name), v1, v2, v3, v4);
}
void Shader::setFloat(std::string_view name, glm::vec2 value) const
void Shader::setFloat(std::string_view name, glm::vec2 value)
{
// Set uniform vec2 data
glUniform2f(findUniform(name), value.x, value.y);
glUniform2f(findUniformLocation(name), value.x, value.y);
}
void Shader::setFloat(std::string_view name, glm::vec3 value) const
void Shader::setFloat(std::string_view name, glm::vec3 value)
{
// Set uniform vec3 data
glUniform3f(findUniform(name), value.x, value.y, value.z);
glUniform3f(findUniformLocation(name), value.x, value.y, value.z);
}
void Shader::setFloat(std::string_view name, glm::vec4 value) const
void Shader::setFloat(std::string_view name, glm::vec4 value)
{
// Set uniform vec4 data
glUniform4f(findUniform(name), value.x, value.y, value.z, value.w);
glUniform4f(findUniformLocation(name), value.x, value.y, value.z, value.w);
}
void Shader::setFloat(std::string_view name, glm::mat3 matrix) const
void Shader::setFloat(std::string_view name, glm::mat3 matrix)
{
// Set uniform mat3 data
glUniformMatrix3fv(findUniform(name), 1, GL_FALSE, glm::value_ptr(matrix));
glUniformMatrix3fv(findUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix));
}
void Shader::setFloat(std::string_view name, glm::mat4 matrix) const
void Shader::setFloat(std::string_view name, glm::mat4 matrix)
{
// Set uniform mat4 data
glUniformMatrix4fv(findUniform(name), 1, GL_FALSE, glm::value_ptr(matrix));
glUniformMatrix4fv(findUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix));
}
void Shader::bind() const
@@ -121,6 +130,8 @@ void Shader::unbind() const
glUseProgram(0);
}
// -----------------------------------------
uint32_t Shader::compileShader(int32_t type, const char* source) const
{
// Create new shader
+12 -8
View File
@@ -4,8 +4,11 @@
* 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
@@ -20,17 +23,17 @@ public:
// Factory function
static std::shared_ptr<Shader> create(std::string_view path);
int32_t findUniform(std::string_view name) const;
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) const;
void setFloat(std::string_view name, float v1, float v2, float v3, float v4) const;
void setFloat(std::string_view name, glm::vec2 value) const;
void setFloat(std::string_view name, glm::vec3 value) const;
void setFloat(std::string_view name, glm::vec4 value) const;
void setFloat(std::string_view name, glm::mat3 matrix) const;
void setFloat(std::string_view name, glm::mat4 matrix) const;
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;
@@ -52,6 +55,7 @@ private:
private:
uint32_t m_id { 0 };
std::unordered_map<std::string_view, uint32_t> m_uniformLocations;
};
// -----------------------------------------
+49 -2
View File
@@ -13,6 +13,8 @@
#include "ruc/meta/assert.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb/stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb/stb_image_write.h"
#include "inferno/asset/texture.h"
@@ -23,6 +25,51 @@ Texture::~Texture()
glDeleteTextures(1, &m_id);
}
// -----------------------------------------
void Texture::savePNG(std::string_view path, std::shared_ptr<Texture> texture)
{
texture->bind();
// Allocate memory
uint32_t dataFormat = texture->m_dataFormat == GL_RGBA ? 4 : 3;
size_t dataSize = texture->m_width * texture->m_height * dataFormat;
std::vector<unsigned char> data(dataSize);
// Read texture data from the GPU
glGetTexImage(GL_TEXTURE_2D, 0, texture->m_dataFormat, texture->m_dataType, data.data());
// Write image to a file
stbi_flip_vertically_on_write(1);
stbi_write_png(
path.data(),
texture->m_width,
texture->m_height,
dataFormat,
data.data(),
texture->m_width * dataFormat);
texture->unbind();
}
void Texture::saveScreenshotPNG(std::string_view path, uint32_t width, uint32_t height)
{
// Allocate memory
size_t dataSize = width * height * 4;
std::vector<unsigned char> data(dataSize);
// Set the pack alignment to 1 (to avoid row alignment issues)
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Read the pixels from the default framebuffer
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
stbi_flip_vertically_on_write(1);
stbi_write_png(path.data(), width, height, 4, data.data(), width * 4);
}
// -----------------------------------------
void Texture::init(uint32_t width, uint32_t height, uint8_t channels)
{
init(width, height,
@@ -283,8 +330,8 @@ void TextureFramebuffer::createImpl()
m_dataType, // Texture source datatype
NULL); // Image data
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Unbind texture object
glBindTexture(GL_TEXTURE_2D, 0);
+3
View File
@@ -22,6 +22,9 @@ 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);
+4 -1
View File
@@ -8,7 +8,7 @@
#include "inferno/asset/texture.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
namespace Inferno {
@@ -22,6 +22,9 @@ void fromJson(const ruc::Json& json, CubemapComponent& value)
if (json.exists("texture") && json.at("texture").type() == ruc::Json::Type::String) {
value.texture = AssetManager::the().load<TextureCubemap>(json.at("texture").asString());
}
if (json.exists("isLight")) {
json.at("isLight").getTo(value.isLight);
}
}
} // namespace Inferno
@@ -18,6 +18,7 @@ namespace Inferno {
struct CubemapComponent {
glm::vec4 color { 1.0f };
std::shared_ptr<Texture> texture;
bool isLight { false };
};
void fromJson(const ruc::Json& json, CubemapComponent& value);
@@ -8,6 +8,7 @@
#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 {
@@ -15,6 +16,9 @@ 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());
}
+1
View File
@@ -16,6 +16,7 @@
namespace Inferno {
struct ModelComponent {
glm::vec4 color { 1.0f };
std::shared_ptr<Model> model;
std::shared_ptr<Texture2D> texture;
};
+127
View File
@@ -0,0 +1,127 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "glm/ext/matrix_float2x2.hpp"
#include "glm/ext/matrix_float3x3.hpp"
#include "glm/ext/matrix_float4x4.hpp"
#include "glm/ext/vector_float2.hpp"
#include "glm/ext/vector_float3.hpp"
#include "glm/ext/vector_float4.hpp"
#include "ruc/json/json.h"
#include "inferno/component/serialize.h"
namespace glm {
void toJson(ruc::Json& json, const vec2& value)
{
json = ruc::Json {
{ value.x, value.y },
};
}
void fromJson(const ruc::Json& json, vec2& value)
{
VERIFY(json.type() == ruc::Json::Type::Array);
auto& values = json.asArray();
VERIFY(values.size() == 2, "glm::vec2 expected 2 values, got: {}", values.size());
value.x = values.at(0).get<float>();
value.y = values.at(1).get<float>();
}
// -----------------------------------------
void toJson(ruc::Json& json, const vec3& value)
{
json = ruc::Json {
{ value.x, value.y, value.z },
};
}
void fromJson(const ruc::Json& json, vec3& value)
{
VERIFY(json.type() == ruc::Json::Type::Array);
auto& values = json.asArray();
VERIFY(values.size() == 3, "glm::vec3 expected 3 values, got: {}", values.size());
value.x = values.at(0).get<float>();
value.y = values.at(1).get<float>();
value.z = values.at(2).get<float>();
}
// -----------------------------------------
void toJson(ruc::Json& json, const vec4& value)
{
json = ruc::Json {
{ value.r, value.g, value.b, value.a },
};
}
void fromJson(const ruc::Json& json, vec4& value)
{
VERIFY(json.type() == ruc::Json::Type::Array);
auto& values = json.asArray();
VERIFY(values.size() == 4, "glm::vec4 expected 4 values, got: {}", values.size());
value.r = values.at(0).get<float>();
value.g = values.at(1).get<float>();
value.b = values.at(2).get<float>();
value.a = values.at(3).get<float>();
}
} // namespace glm
// -----------------------------------------
void ruc::format::Formatter<glm::vec2>::format(Builder& builder, glm::vec2 value) const
{
return Formatter<std::vector<float>>::format(builder, { value.x, value.y });
}
void ruc::format::Formatter<glm::vec3>::format(Builder& builder, glm::vec3 value) const
{
return Formatter<std::vector<float>>::format(builder, { value.x, value.y, value.z });
}
void ruc::format::Formatter<glm::vec4>::format(Builder& builder, glm::vec4 value) const
{
return Formatter<std::vector<float>>::format(builder, { value.x, value.y, value.z, value.w });
}
void ruc::format::Formatter<glm::mat2>::format(Builder& builder, glm::mat2 value) const
{
builder.putString("mat2 ");
Formatter<glm::vec2>::format(builder, value[0]);
builder.putString("\n ");
return Formatter<glm::vec2>::format(builder, value[1]);
}
void ruc::format::Formatter<glm::mat3>::format(Builder& builder, glm::mat3 value) const
{
builder.putString("mat3 ");
Formatter<glm::vec3>::format(builder, value[0]);
builder.putString("\n ");
Formatter<glm::vec3>::format(builder, value[1]);
builder.putString("\n ");
return Formatter<glm::vec3>::format(builder, value[2]);
}
void ruc::format::Formatter<glm::mat4>::format(Builder& builder, glm::mat4 value) const
{
builder.putString("mat4 ");
Formatter<glm::vec4>::format(builder, value[0]);
builder.putString("\n ");
Formatter<glm::vec4>::format(builder, value[1]);
builder.putString("\n ");
Formatter<glm::vec4>::format(builder, value[2]);
builder.putString("\n ");
return Formatter<glm::vec4>::format(builder, value[3]);
}
+58
View File
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "glm/ext/matrix_float2x2.hpp"
#include "glm/ext/matrix_float3x3.hpp"
#include "glm/ext/matrix_float4x4.hpp"
#include "glm/ext/vector_float2.hpp"
#include "glm/ext/vector_float3.hpp"
#include "glm/ext/vector_float4.hpp"
#include "ruc/json/json.h"
namespace glm {
void toJson(ruc::Json& json, const vec2& value);
void fromJson(const ruc::Json& json, vec2& value);
void toJson(ruc::Json& json, const vec3& value);
void fromJson(const ruc::Json& json, vec3& value);
void toJson(ruc::Json& json, const vec4& value);
void fromJson(const ruc::Json& json, vec4& value);
} // namespace glm
template<>
struct ruc::format::Formatter<glm::vec2> : Formatter<std::vector<float>> {
void format(Builder& builder, glm::vec2 value) const;
};
template<>
struct ruc::format::Formatter<glm::vec3> : Formatter<std::vector<float>> {
void format(Builder& builder, glm::vec3 value) const;
};
template<>
struct ruc::format::Formatter<glm::vec4> : Formatter<std::vector<float>> {
void format(Builder& builder, glm::vec4 value) const;
};
template<>
struct ruc::format::Formatter<glm::mat2> : Formatter<glm::vec2> {
void format(Builder& builder, glm::mat2 value) const;
};
template<>
struct ruc::format::Formatter<glm::mat3> : Formatter<glm::vec3> {
void format(Builder& builder, glm::mat3 value) const;
};
template<>
struct ruc::format::Formatter<glm::mat4> : Formatter<glm::vec4> {
void format(Builder& builder, glm::mat4 value) const;
};
+4 -25
View File
@@ -4,9 +4,12 @@
* SPDX-License-Identifier: MIT
*/
#include "inferno/component/spritecomponent.h"
#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"
namespace Inferno {
@@ -23,27 +26,3 @@ void fromJson(const ruc::Json& json, SpriteComponent& value)
}
} // 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
-7
View File
@@ -23,10 +23,3 @@ struct SpriteComponent {
void fromJson(const ruc::Json& json, SpriteComponent& value);
} // namespace Inferno
namespace glm {
void toJson(ruc::Json& json, const vec4& value);
void fromJson(const ruc::Json& json, vec4& value);
} // namespace glm
+1 -50
View File
@@ -7,6 +7,7 @@
#include "ruc/format/format.h"
#include "ruc/json/json.h"
#include "inferno/component/serialize.h"
#include "inferno/component/transformcomponent.h"
namespace Inferno {
@@ -28,56 +29,6 @@ void fromJson(const ruc::Json& json, TransformComponent& value)
} // namespace Inferno
namespace glm {
void toJson(ruc::Json& json, const vec3& value)
{
json = ruc::Json {
{ value.x, value.y, value.z },
};
}
void fromJson(const ruc::Json& json, vec3& value)
{
VERIFY(json.type() == ruc::Json::Type::Array);
auto& values = json.asArray();
VERIFY(values.size() == 3, "glm::vec3 expects 3 values, not {}", values.size());
value.x = values.at(0).get<float>();
value.y = values.at(1).get<float>();
value.z = values.at(2).get<float>();
}
} // namespace glm
void ruc::format::Formatter<glm::vec2>::format(Builder& builder, glm::vec2 value) const
{
return Formatter<std::vector<float>>::format(builder, { value.x, value.y });
}
void ruc::format::Formatter<glm::vec3>::format(Builder& builder, glm::vec3 value) const
{
return Formatter<std::vector<float>>::format(builder, { value.x, value.y, value.z });
}
void ruc::format::Formatter<glm::vec4>::format(Builder& builder, glm::vec4 value) const
{
return Formatter<std::vector<float>>::format(builder, { value.x, value.y, value.z, value.w });
}
void ruc::format::Formatter<glm::mat4>::format(Builder& builder, glm::mat4 value) const
{
builder.putString("mat4 ");
Formatter<glm::vec4>::format(builder, value[0]);
builder.putString("\n ");
Formatter<glm::vec4>::format(builder, value[1]);
builder.putString("\n ");
Formatter<glm::vec4>::format(builder, value[2]);
builder.putString("\n ");
return Formatter<glm::vec4>::format(builder, value[3]);
}
void ruc::format::Formatter<Inferno::TransformComponent>::format(Builder& builder, Inferno::TransformComponent value) const
{
builder.putString("transform ");
@@ -6,9 +6,6 @@
#pragma once
#include <cstdint> // uint32_t
#include <optional>
#include "entt/entity/entity.hpp" // entt::null
#include "entt/entity/fwd.hpp" // entt::entity
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
@@ -31,33 +28,6 @@ void fromJson(const ruc::Json& json, TransformComponent& value);
} // namespace Inferno
namespace glm {
void toJson(ruc::Json& json, const vec3& value);
void fromJson(const ruc::Json& json, vec3& value);
} // namespace glm
template<>
struct ruc::format::Formatter<glm::vec2> : Formatter<std::vector<float>> {
void format(Builder& builder, glm::vec2 value) const;
};
template<>
struct ruc::format::Formatter<glm::vec3> : Formatter<std::vector<float>> {
void format(Builder& builder, glm::vec3 value) const;
};
template<>
struct ruc::format::Formatter<glm::vec4> : Formatter<std::vector<float>> {
void format(Builder& builder, glm::vec4 value) const;
};
template<>
struct ruc::format::Formatter<glm::mat4> : Formatter<glm::vec4> {
void format(Builder& builder, glm::mat4 value) const;
};
template<>
struct ruc::format::Formatter<Inferno::TransformComponent> : Formatter<glm::vec3> {
void format(Builder& builder, Inferno::TransformComponent value) const;
+17 -14
View File
@@ -14,24 +14,27 @@ namespace Inferno {
class KeyEvent : public Event {
public:
inline int getKey() const { return m_key; }
int getKey() const { return m_key; }
int getMods() const { return m_mods; }
EVENT_CLASS_CATEGORY(InputEventCategory | KeyEventCategory)
protected:
KeyEvent(int key)
KeyEvent(int key, int mods)
: m_key(key)
, m_mods(mods)
{
}
private:
int m_key;
int m_key { 0 };
int m_mods { 0 };
};
class KeyPressEvent : public KeyEvent {
class KeyPressEvent final : public KeyEvent {
public:
KeyPressEvent(int key)
: KeyEvent(key)
KeyPressEvent(int key, int mods)
: KeyEvent(key, mods)
{
}
@@ -45,10 +48,10 @@ public:
EVENT_CLASS_TYPE(KeyPress)
};
class KeyReleaseEvent : public KeyEvent {
class KeyReleaseEvent final : public KeyEvent {
public:
KeyReleaseEvent(int key)
: KeyEvent(key)
KeyReleaseEvent(int key, int mods)
: KeyEvent(key, mods)
{
}
@@ -59,13 +62,13 @@ public:
return ss.str();
}
EVENT_CLASS_TYPE(KeyPress)
EVENT_CLASS_TYPE(KeyRelease)
};
class KeyRepeatEvent : public KeyEvent {
class KeyRepeatEvent final : public KeyEvent {
public:
KeyRepeatEvent(int key)
: KeyEvent(key)
KeyRepeatEvent(int key, int mods)
: KeyEvent(key, mods)
{
}
@@ -76,7 +79,7 @@ public:
return ss.str();
}
EVENT_CLASS_TYPE(KeyPress)
EVENT_CLASS_TYPE(KeyRepeat)
};
} // namespace Inferno
+19
View File
@@ -139,14 +139,33 @@ static std::unordered_map<std::string_view, int> keys({
{ MAP_KEY(GLFW_KEY_MENU) },
});
static std::unordered_map<std::string_view, int> modifiers({
{ MAP_KEY(GLFW_MOD_SHIFT) },
{ MAP_KEY(GLFW_MOD_CONTROL) },
{ MAP_KEY(GLFW_MOD_ALT) },
{ MAP_KEY(GLFW_MOD_SUPER) },
{ MAP_KEY(GLFW_MOD_CAPS_LOCK) }, // State, not really a modifier
{ MAP_KEY(GLFW_MOD_NUM_LOCK) }, // State, not really a modifier
});
// -----------------------------------------
// Example usage:
// event.getKey() == keyCode("GLFW_KEY_ESCAPE")
int keyCode(std::string_view name)
{
VERIFY(keys.find(name) != keys.end(), "could not find key code: {}", name);
return keys.at(name);
}
// Example usage:
// event.getMods() & keyMod("GLFW_MOD_SHIFT")
int keyMod(std::string_view name)
{
VERIFY(modifiers.find(name) != modifiers.end(), "could not find key modifier: {}", name);
return modifiers.at(name);
}
std::string_view keyName(int key)
{
auto it = std::find_if(keys.begin(), keys.end(), [key](const auto& keybind) {
+3 -1
View File
@@ -13,6 +13,8 @@
namespace Inferno {
int keyCode(std::string_view name);
std::string_view keyName(int);
int keyMod(std::string_view name);
std::string_view keyName(int key);
} // namespace Inferno
+139 -30
View File
@@ -4,6 +4,12 @@
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint8_t, uint32_t
#include <memory> // std::shared_ptr
#include <string>
#include <utility> // std::pair
#include "glad/glad.h"
#include "ruc/meta/assert.h"
@@ -36,7 +42,7 @@ uint32_t BufferElement::getTypeGL() const
return BufferElement::getTypeGL(m_type);
}
uint32_t BufferElement::getTypeSize(const BufferElementType type)
uint32_t BufferElement::getTypeSize(BufferElementType type)
{
switch (type) {
case BufferElementType::None:
@@ -61,10 +67,10 @@ uint32_t BufferElement::getTypeSize(const BufferElementType type)
case BufferElementType::Vec3:
case BufferElementType::Vec4:
return sizeof(float) * getTypeCount(type);
case BufferElementType::VecDouble:
case BufferElementType::VecDouble2:
case BufferElementType::VecDouble3:
case BufferElementType::VecDouble4:
case BufferElementType::Double:
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
return sizeof(double) * getTypeCount(type);
case BufferElementType::Mat2:
case BufferElementType::Mat3:
@@ -80,7 +86,7 @@ uint32_t BufferElement::getTypeSize(const BufferElementType type)
return 0;
}
uint32_t BufferElement::getTypeCount(const BufferElementType type)
uint32_t BufferElement::getTypeCount(BufferElementType type)
{
switch (type) {
case BufferElementType::None:
@@ -89,25 +95,25 @@ uint32_t BufferElement::getTypeCount(const BufferElementType type)
case BufferElementType::Int:
case BufferElementType::Uint:
case BufferElementType::Float:
case BufferElementType::VecDouble:
case BufferElementType::Double:
return 1;
case BufferElementType::Bool2:
case BufferElementType::Int2:
case BufferElementType::Uint2:
case BufferElementType::Vec2:
case BufferElementType::VecDouble2:
case BufferElementType::Vec2Double:
return 2;
case BufferElementType::Bool3:
case BufferElementType::Int3:
case BufferElementType::Uint3:
case BufferElementType::Vec3:
case BufferElementType::VecDouble3:
case BufferElementType::Vec3Double:
return 3;
case BufferElementType::Bool4:
case BufferElementType::Int4:
case BufferElementType::Uint4:
case BufferElementType::Vec4:
case BufferElementType::VecDouble4:
case BufferElementType::Vec4Double:
return 4;
case BufferElementType::Mat2:
return 2 * 2;
@@ -127,7 +133,7 @@ uint32_t BufferElement::getTypeCount(const BufferElementType type)
return 0;
}
uint32_t BufferElement::getTypeGL(const BufferElementType type)
uint32_t BufferElement::getTypeGL(BufferElementType type)
{
switch (type) {
case BufferElementType::None:
@@ -152,10 +158,10 @@ uint32_t BufferElement::getTypeGL(const BufferElementType type)
case BufferElementType::Vec3:
case BufferElementType::Vec4:
return GL_FLOAT;
case BufferElementType::VecDouble:
case BufferElementType::VecDouble2:
case BufferElementType::VecDouble3:
case BufferElementType::VecDouble4:
case BufferElementType::Double:
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
return GL_DOUBLE;
case BufferElementType::Mat2:
case BufferElementType::Mat3:
@@ -171,6 +177,58 @@ uint32_t BufferElement::getTypeGL(const BufferElementType type)
return 0;
}
uint32_t BufferElement::getGLTypeSize(uint32_t type)
{
switch (type) {
case GL_BOOL:
case GL_INT:
case GL_UNSIGNED_INT:
case GL_FLOAT:
return 4;
case GL_BOOL_VEC2:
case GL_INT_VEC2:
case GL_UNSIGNED_INT_VEC2:
case GL_FLOAT_VEC2:
return 4 * 2;
case GL_BOOL_VEC3:
case GL_INT_VEC3:
case GL_UNSIGNED_INT_VEC3:
case GL_FLOAT_VEC3:
return 4 * 3;
case GL_BOOL_VEC4:
case GL_INT_VEC4:
case GL_UNSIGNED_INT_VEC4:
case GL_FLOAT_VEC4:
return 4 * 4;
case GL_FLOAT_MAT2:
return 4 * 2 * 2;
case GL_FLOAT_MAT3:
return 4 * 3 * 3;
case GL_FLOAT_MAT4:
return 4 * 4 * 4;
case GL_DOUBLE:
return 8;
case GL_DOUBLE_VEC2:
return 8 * 2;
case GL_DOUBLE_VEC3:
return 8 * 3;
case GL_DOUBLE_VEC4:
return 8 * 4;
case GL_DOUBLE_MAT2:
return 8 * 2 * 2;
case GL_DOUBLE_MAT3:
return 8 * 3 * 3;
case GL_DOUBLE_MAT4:
return 8 * 4 * 4;
default:
VERIFY_NOT_REACHED();
};
return 0;
}
// -----------------------------------------
BufferLayout::BufferLayout(const std::initializer_list<BufferElement>& elements)
@@ -184,7 +242,7 @@ void BufferLayout::calculateOffsetsAndStride()
m_stride = 0;
for (auto& element : m_elements) {
element.setOffset(m_stride);
m_stride += element.getSize();
m_stride += element.size();
}
}
@@ -202,7 +260,7 @@ VertexBuffer::VertexBuffer(size_t size, float* vertices)
bind();
// Upload data to the GPU
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_DYNAMIC_DRAW);
unbind();
}
@@ -242,7 +300,7 @@ IndexBuffer::IndexBuffer(uint32_t* indices, size_t size)
bind();
// Upload data to the GPU
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_DYNAMIC_DRAW);
unbind();
}
@@ -297,8 +355,8 @@ void VertexArray::unbind() const
void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
{
const auto& layout = vertexBuffer->getLayout();
VERIFY(layout.getElements().size(), "VertexBuffer has no layout");
const auto& layout = vertexBuffer->layout();
VERIFY(layout.elements().size(), "VertexBuffer has no layout");
bind();
vertexBuffer->bind();
@@ -306,20 +364,71 @@ void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
uint32_t index = 0;
for (const auto& element : layout) {
glEnableVertexAttribArray(index);
glVertexAttribPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
element.getNormalized() ? GL_TRUE : GL_FALSE,
layout.getStride(),
reinterpret_cast<const void*>(element.getOffset()));
switch (element.type()) {
case BufferElementType::None:
break;
case BufferElementType::Int:
case BufferElementType::Int2:
case BufferElementType::Int3:
case BufferElementType::Int4:
case BufferElementType::Uint:
case BufferElementType::Uint2:
case BufferElementType::Uint3:
case BufferElementType::Uint4: {
glVertexAttribIPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
layout.stride(),
reinterpret_cast<const void*>(element.offset()));
break;
}
case BufferElementType::Bool:
case BufferElementType::Bool2:
case BufferElementType::Bool3:
case BufferElementType::Bool4:
case BufferElementType::Float:
case BufferElementType::Vec2:
case BufferElementType::Vec3:
case BufferElementType::Vec4:
case BufferElementType::Mat2:
case BufferElementType::Mat3:
case BufferElementType::Mat4: {
glVertexAttribPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
element.normalized() ? GL_TRUE : GL_FALSE,
layout.stride(),
reinterpret_cast<const void*>(element.offset()));
break;
}
case BufferElementType::Double:
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
case BufferElementType::MatDouble2:
case BufferElementType::MatDouble3:
case BufferElementType::MatDouble4: {
glVertexAttribLPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
layout.stride(),
reinterpret_cast<const void*>(element.offset()));
break;
}
default:
VERIFY_NOT_REACHED();
};
index++;
}
m_vertexBuffers.push_back(std::move(vertexBuffer));
unbind();
vertexBuffer->unbind();
m_vertexBuffers.push_back(std::move(vertexBuffer));
}
void VertexArray::setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer)
@@ -327,10 +436,10 @@ void VertexArray::setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer)
bind();
indexBuffer->bind();
m_indexBuffer = std::move(indexBuffer);
unbind();
indexBuffer->unbind();
m_indexBuffer = std::move(indexBuffer);
}
} // namespace Inferno
+24 -23
View File
@@ -7,22 +7,22 @@
#pragma once
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint32_t
#include <cstdint> // int32_t, uint8_t, uint32_t
#include <memory> // std::shared_ptr
#include <string> // std::string
#include <vector> // std::vector
#include <string>
#include <vector>
namespace Inferno {
// clang-format off
// https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)
enum class BufferElementType {
enum class BufferElementType : uint8_t {
None = 0,
Bool, Bool2, Bool3, Bool4, // bvec
Int, Int2, Int3, Int4, // ivec
Uint, Uint2, Uint3, Uint4, // uvec
Float, Vec2, Vec3, Vec4, // vec
VecDouble, VecDouble2, VecDouble3, VecDouble4, // dvec
Double, Vec2Double, Vec3Double, Vec4Double, // dvec
Mat2, Mat3, Mat4, // mat
MatDouble2, MatDouble3, MatDouble4, // dmat
};
@@ -39,21 +39,22 @@ public:
uint32_t getTypeSize() const;
uint32_t getTypeCount() const;
uint32_t getTypeGL() const;
static uint32_t getTypeSize(const BufferElementType type);
static uint32_t getTypeCount(const BufferElementType type);
static uint32_t getTypeGL(const BufferElementType type);
static uint32_t getTypeSize(BufferElementType type);
static uint32_t getTypeCount(BufferElementType type);
static uint32_t getTypeGL(BufferElementType type);
static uint32_t getGLTypeSize(uint32_t type);
BufferElementType getType() const { return m_type; }
std::string getName() const { return m_name; }
uint32_t getSize() const { return m_size; }
uint32_t getOffset() const { return m_offset; }
bool getNormalized() const { return m_normalized; }
BufferElementType type() const { return m_type; }
std::string name() const { return m_name; }
uint32_t size() const { return m_size; }
uint32_t offset() const { return m_offset; }
bool normalized() const { return m_normalized; }
void setType(const BufferElementType& type) { m_type = type; }
void setType(BufferElementType type) { m_type = type; }
void setName(const std::string& name) { m_name = name; }
void setSize(const uint32_t& size) { m_size = size; }
void setOffset(const uint32_t& offset) { m_offset = offset; }
void setNormalized(const bool& normalized) { m_normalized = normalized; }
void setSize(uint32_t size) { m_size = size; }
void setOffset(uint32_t offset) { m_offset = offset; }
void setNormalized(bool normalized) { m_normalized = normalized; }
private:
BufferElementType m_type;
@@ -72,8 +73,8 @@ public:
BufferLayout(const std::initializer_list<BufferElement>& elements);
~BufferLayout() = default;
const std::vector<BufferElement>& getElements() const { return m_elements; }
uint32_t getStride() const { return m_stride; }
const std::vector<BufferElement>& elements() const { return m_elements; }
uint32_t stride() const { return m_stride; }
// Iterators
std::vector<BufferElement>::iterator begin() { return m_elements.begin(); }
@@ -103,9 +104,9 @@ public:
void uploadData(const void* data, uint32_t size);
const BufferLayout& getLayout() const { return m_layout; }
const BufferLayout& layout() const { return m_layout; }
inline void setLayout(const BufferLayout& layout) { m_layout = layout; }
void setLayout(const BufferLayout& layout) { m_layout = layout; }
private:
uint32_t m_id { 0 };
@@ -125,7 +126,7 @@ public:
void uploadData(const void* data, uint32_t size);
uint32_t getCount() const { return m_count; }
uint32_t count() const { return m_count; }
private:
uint32_t m_id { 0 };
@@ -147,7 +148,7 @@ public:
void setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer);
std::shared_ptr<VertexBuffer> at(size_t i) const { return m_vertexBuffers.at(i); }
std::shared_ptr<IndexBuffer> getIndexBuffer() const { return m_indexBuffer; }
std::shared_ptr<IndexBuffer> indexBuffer() const { return m_indexBuffer; }
private:
uint32_t m_id { 0 };
+40 -7
View File
@@ -36,6 +36,14 @@ Framebuffer::~Framebuffer()
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
@@ -86,22 +94,47 @@ void Framebuffer::createTextures()
bind();
auto it = m_attachments.begin();
uint8_t color_attachment = 0;
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::Color) {
if (type.type == Type::RGB8) {
// Set color attachment 0 out of 32
m_textures[i] = (TextureFramebuffer::create(
"", m_width, m_height, GL_RGB, GL_RGB));
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + color_attachment, GL_TEXTURE_2D, m_textures[i]->id(), 0);
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) {
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);
@@ -117,7 +150,7 @@ void Framebuffer::createTextures()
}
}
VERIFY(color_attachment <= 32, "maximum color attachments was exceeded: {}/32", color_attachment);
VERIFY(m_colorAttachmentCount <= 32, "maximum color attachments was exceeded: {}/32", m_colorAttachmentCount);
check();
unbind();
+9 -3
View File
@@ -24,11 +24,14 @@ public:
None = 0,
// Color
RGBA8 = 1,
RGB8 = 1,
RGBA8 = 2,
RGBA16F = 3,
RGBA32F = 4,
// Depth/stencil
Depth32F = 2,
Depth24Stencil8 = 3,
Depth24Stencil8 = 5,
Depth32F = 6,
// Defaults
Color = RGBA8,
@@ -59,12 +62,14 @@ public:
// 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; }
@@ -87,6 +92,7 @@ private:
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 };
+24 -2
View File
@@ -4,10 +4,12 @@
* SPDX-License-Identifier: MIT
*/
#include <memory> // std::shared_ptr
#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"
@@ -42,7 +44,7 @@ void RenderCommand::clearColor(const glm::vec4& color)
void RenderCommand::drawIndexed(std::shared_ptr<VertexArray> vertexArray, uint32_t indexCount)
{
uint32_t count = indexCount ? indexCount : vertexArray->getIndexBuffer()->getCount();
uint32_t count = indexCount ? indexCount : vertexArray->indexBuffer()->count();
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
}
@@ -57,6 +59,26 @@ void RenderCommand::setDepthTest(bool enabled)
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;
+3 -1
View File
@@ -6,7 +6,8 @@
#pragma once
#include <memory> // std::shadred_ptr
#include <cstdint> // int32_t, uint32_t
#include <memory> // std::shadred_ptr
#include "glm/ext/vector_float4.hpp" // glm::vec4
@@ -25,6 +26,7 @@ public:
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();
+127 -46
View File
@@ -8,6 +8,7 @@
#include <span>
#include "glad/glad.h"
#include "glm/ext/vector_float4.hpp" // glm::vec4
#include "ruc/format/log.h"
#include "inferno/asset/asset-manager.h"
@@ -33,9 +34,6 @@ void Renderer<T>::endScene()
// -----------------------------------------
template<typename T>
uint32_t Renderer<T>::m_maxSupportedTextureSlots = 0;
template<typename T>
void Renderer<T>::initialize()
{
@@ -67,7 +65,6 @@ void Renderer<T>::initialize()
template<typename T>
void Renderer<T>::destroy()
{
delete[] m_vertexBufferBase;
}
template<typename T>
@@ -163,13 +160,14 @@ void Renderer<T>::flush()
uploadElementBuffer();
// Upload vertex data to GPU
m_vertexArray->at(0)->uploadData(m_vertexBufferBase, m_vertexIndex * sizeof(T));
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);
@@ -181,7 +179,7 @@ void Renderer<T>::startBatch()
{
m_vertexIndex = 0;
m_elementIndex = 0;
m_vertexBufferPtr = m_vertexBufferBase;
m_vertexBufferPtr = m_vertexBufferBase.get();
m_textureSlotIndex = 1;
}
@@ -196,6 +194,15 @@ void Renderer<T>::nextBatch()
// -----------------------------------------
Renderer2D::Renderer2D(s)
{
Renderer2D::initialize();
}
Renderer2D::~Renderer2D()
{
}
void Renderer2D::initialize()
{
Renderer::initialize();
@@ -203,8 +210,8 @@ Renderer2D::Renderer2D(s)
// CPU
// Create array for storing quads vertices
m_vertexBufferBase = new QuadVertex[maxVertices];
m_vertexBufferPtr = m_vertexBufferBase;
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 };
@@ -215,26 +222,21 @@ Renderer2D::Renderer2D(s)
// ---------------------------------
// 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::Float, "a_textureIndex" },
{ BufferElementType::Uint, "a_textureIndex" },
});
m_vertexArray->addVertexBuffer(vertexBuffer);
ruc::info("Renderer2D initialized");
}
void Renderer2D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
{
m_shader->bind();
m_shader->setFloat("u_projectionView", cameraProjection * cameraView);
m_shader->unbind();
}
void Renderer2D::drawQuad(const TransformComponent& transform, glm::vec4 color)
{
drawQuad(transform, color, nullptr);
@@ -271,7 +273,7 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color,
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
m_vertexBufferPtr->color = color[i];
m_vertexBufferPtr->textureCoordinates = textureCoordinates[i];
m_vertexBufferPtr->textureIndex = static_cast<float>(textureUnitIndex);
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
@@ -281,12 +283,21 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color,
void Renderer2D::loadShader()
{
m_shader = AssetManager::the().load<Shader>("assets/glsl/batch-quad");
m_shader = AssetManager::the().load<Shader>("assets/glsl/batch-2d");
}
// -----------------------------------------
RendererCubemap::RendererCubemap(s)
{
RendererCubemap::initialize();
}
RendererCubemap::~RendererCubemap()
{
}
void RendererCubemap::initialize()
{
Renderer::initialize();
@@ -294,8 +305,8 @@ RendererCubemap::RendererCubemap(s)
// CPU
// Create array for storing quads vertices
m_vertexBufferBase = new CubemapVertex[maxVertices];
m_vertexBufferPtr = m_vertexBufferBase;
m_vertexBufferBase = std::make_unique<CubemapVertex[]>(maxVertices);
m_vertexBufferPtr = m_vertexBufferBase.get();
// Set default cubemap vertex positions
@@ -345,7 +356,7 @@ RendererCubemap::RendererCubemap(s)
vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec4, "a_color" },
{ BufferElementType::Float, "a_textureIndex" },
{ BufferElementType::Uint, "a_textureIndex" },
});
m_vertexArray->addVertexBuffer(vertexBuffer);
@@ -386,7 +397,7 @@ void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::mat4
for (uint32_t i = 0; i < vertexPerQuad * quadPerCube; i++) {
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
m_vertexBufferPtr->color = color[i % 4];
m_vertexBufferPtr->textureIndex = static_cast<float>(textureUnitIndex);
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
@@ -409,8 +420,8 @@ RendererFont::RendererFont(s)
// CPU
// Create array for storing quads vertices
m_vertexBufferBase = new SymbolVertex[maxVertices];
m_vertexBufferPtr = m_vertexBufferBase;
m_vertexBufferBase = std::make_unique<SymbolVertex[]>(maxVertices);
m_vertexBufferPtr = m_vertexBufferBase.get();
// ---------------------------------
// GPU
@@ -423,7 +434,7 @@ RendererFont::RendererFont(s)
{ BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec4, "a_color" },
{ BufferElementType::Vec2, "a_textureCoordinates" },
{ BufferElementType::Float, "a_textureIndex" },
{ BufferElementType::Uint, "a_textureIndex" },
{ BufferElementType::Float, "a_width" },
{ BufferElementType::Float, "a_edge" },
{ BufferElementType::Float, "a_borderWidth" },
@@ -436,6 +447,10 @@ RendererFont::RendererFont(s)
ruc::info("RendererFont initialized");
}
RendererFont::~RendererFont()
{
}
void RendererFont::drawSymbol(std::array<SymbolVertex, vertexPerQuad>& symbolQuad, std::shared_ptr<Texture> texture)
{
// Create a new batch if the quad limit has been reached
@@ -450,7 +465,7 @@ void RendererFont::drawSymbol(std::array<SymbolVertex, vertexPerQuad>& symbolQua
m_vertexBufferPtr->quad.position = symbolQuad[i].quad.position;
m_vertexBufferPtr->quad.color = symbolQuad[i].quad.color;
m_vertexBufferPtr->quad.textureCoordinates = symbolQuad[i].quad.textureCoordinates;
m_vertexBufferPtr->quad.textureIndex = static_cast<float>(textureUnitIndex);
m_vertexBufferPtr->quad.textureIndex = textureUnitIndex;
m_vertexBufferPtr->width = symbolQuad[i].width;
m_vertexBufferPtr->edge = symbolQuad[i].edge;
@@ -481,33 +496,39 @@ Renderer3D::Renderer3D(s)
// CPU
// Create array for storing quads vertices
m_vertexBufferBase = new Vertex[maxVertices];
m_vertexBufferPtr = m_vertexBufferBase;
m_vertexBufferBase = std::make_unique<Vertex[]>(maxVertices);
m_vertexBufferPtr = m_vertexBufferBase.get();
// ---------------------------------
// 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::Float, "a_textureIndex" },
{ BufferElementType::Uint, "a_textureIndex" },
});
m_vertexArray->addVertexBuffer(vertexBuffer);
ruc::info("Renderer3D initialized");
}
void Renderer3D::drawModel(std::span<const Vertex> vertices, std::span<const uint32_t> elements, const TransformComponent& transform, std::shared_ptr<Texture> texture)
Renderer3D::~Renderer3D()
{
}
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");
VERIFY(elements.size() <= maxElements, "model elements too big for buffer");
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) {
@@ -517,11 +538,13 @@ void Renderer3D::drawModel(std::span<const Vertex> vertices, std::span<const uin
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 = vertex.normal;
m_vertexBufferPtr->normal = normalMatrix * vertex.normal; // take non-uniform scaling into consideration
m_vertexBufferPtr->color = color;
m_vertexBufferPtr->textureCoordinates = vertex.textureCoordinates;
m_vertexBufferPtr->textureIndex = static_cast<float>(textureUnitIndex);
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
@@ -536,33 +559,26 @@ void Renderer3D::drawModel(std::span<const Vertex> vertices, std::span<const uin
m_elementIndex += elements.size();
}
void Renderer3D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
{
m_shader->bind();
m_shader->setFloat("u_projectionView", cameraProjection * cameraView);
m_shader->unbind();
}
void Renderer3D::createElementBuffer()
{
// ---------------------------------
// CPU
// Create array for storing quads vertices
m_elementBufferBase = new uint32_t[maxElements];
m_elementBufferPtr = m_elementBufferBase;
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, sizeof(uint32_t) * maxElements);
auto indexBuffer = std::make_shared<IndexBuffer>(m_elementBufferBase.get(), sizeof(uint32_t) * maxElements);
m_vertexArray->setIndexBuffer(indexBuffer);
}
void Renderer3D::uploadElementBuffer()
{
m_vertexArray->getIndexBuffer()->uploadData(m_elementBufferBase, m_elementIndex * sizeof(uint32_t));
m_vertexArray->indexBuffer()->uploadData(m_elementBufferBase.get(), m_elementIndex * sizeof(uint32_t));
}
void Renderer3D::loadShader()
@@ -573,7 +589,72 @@ void Renderer3D::loadShader()
void Renderer3D::startBatch()
{
Renderer<Vertex>::startBatch();
m_elementBufferPtr = m_elementBufferBase;
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
+79 -30
View File
@@ -16,24 +16,25 @@
#include "glm/ext/vector_float4.hpp" // glm::vec4
#include "ruc/singleton.h"
#include "inferno/asset/shader.h"
namespace Inferno {
class Shader;
class Texture;
class TransformComponent;
class VertexArray;
struct QuadVertex {
glm::vec3 position { 0.0f, 0.0f, 0.0f };
glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f };
glm::vec2 textureCoordinates { 0.0f, 0.0f };
float textureIndex = 0;
glm::vec3 position { 0.0f };
glm::vec4 color { 1.0f };
glm::vec2 textureCoordinates { 0.0f };
uint32_t textureIndex { 0 };
};
struct CubemapVertex {
glm::vec3 position { 0.0f, 0.0f, 0.0f };
glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f };
float textureIndex = 0;
glm::vec3 position { 0.0f };
glm::vec4 color { 1.0f };
uint32_t textureIndex { 0 };
};
struct SymbolVertex {
@@ -45,16 +46,17 @@ struct SymbolVertex {
// Outline
float borderWidth = 0.7f;
float borderEdge = 0.1f;
glm::vec4 borderColor { 1.0f, 1.0f, 1.0f, 1.0f };
glm::vec4 borderColor { 1.0f };
// Dropshadow
float offset = 0.0f;
};
struct Vertex {
glm::vec3 position { 0.0f, 0.0f, 0.0f };
glm::vec3 normal { 1.0f, 1.0f, 1.0f };
glm::vec2 textureCoordinates { 0.0f, 0.0f };
float textureIndex = 0;
glm::vec3 position { 0.0f };
glm::vec3 normal { 1.0f };
glm::vec4 color { 1.0f };
glm::vec2 textureCoordinates { 0.0f };
uint32_t textureIndex { 0 };
};
// -------------------------------------
@@ -78,6 +80,8 @@ public:
void setEnableDepthBuffer(bool state) { m_enableDepthBuffer = state; }
uint32_t shaderID() const { return m_shader->id(); }
protected:
Renderer() {}
virtual ~Renderer() { destroy(); };
@@ -101,16 +105,17 @@ protected:
// CPU quad vertices
uint32_t m_vertexIndex { 0 };
uint32_t m_elementIndex { 0 };
T* m_vertexBufferBase { nullptr };
std::unique_ptr<T[]> m_vertexBufferBase { nullptr };
T* m_vertexBufferPtr { nullptr };
// Texture units
static uint32_t m_maxSupportedTextureSlots;
static inline uint32_t m_maxSupportedTextureSlots { 0 };
uint32_t m_textureSlotIndex { 1 };
std::array<std::shared_ptr<Texture>, maxTextureSlots> m_textureSlots;
// GPU objects
bool m_enableDepthBuffer { true };
uint32_t m_colorAttachmentCount { 1 };
std::shared_ptr<Shader> m_shader;
std::shared_ptr<VertexArray> m_vertexArray;
};
@@ -123,31 +128,34 @@ protected:
// -------------------------------------
class Renderer2D final
class Renderer2D
: public Renderer<QuadVertex>
, public ruc::Singleton<Renderer2D> {
public:
Renderer2D(s);
virtual ~Renderer2D() = default;
virtual ~Renderer2D();
using Singleton<Renderer2D>::destroy;
virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) override;
void drawQuad(const TransformComponent& transform, glm::vec4 color);
void drawQuad(const TransformComponent& transform, glm::mat4 color);
void drawQuad(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture);
void drawQuad(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture);
private:
void loadShader() override;
protected:
Renderer2D() {} // Needed for derived classes
void initialize();
// Default quad vertex positions
glm::vec4 m_vertexPositions[vertexPerQuad];
private:
virtual void loadShader() override;
};
// -------------------------------------
class RendererCubemap final
class RendererCubemap
: public Renderer<CubemapVertex>
, public ruc::Singleton<RendererCubemap> {
public:
@@ -155,7 +163,7 @@ public:
public:
RendererCubemap(s);
virtual ~RendererCubemap() = default;
virtual ~RendererCubemap();
using Singleton<RendererCubemap>::destroy;
@@ -164,8 +172,13 @@ public:
void drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture);
void drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture);
protected:
RendererCubemap() {} // Needed for derived classes
void initialize();
private:
void loadShader() override;
virtual void loadShader() override;
// Default cubemap vertex positions
glm::vec4 m_vertexPositions[vertexPerQuad * quadPerCube];
@@ -178,7 +191,7 @@ class RendererFont final
, public ruc::Singleton<RendererFont> {
public:
RendererFont(s);
virtual ~RendererFont() = default;
virtual ~RendererFont();
using Singleton<RendererFont>::destroy;
@@ -195,13 +208,11 @@ class Renderer3D final
, public ruc::Singleton<Renderer3D> {
public:
Renderer3D(s);
virtual ~Renderer3D() = default;
virtual ~Renderer3D();
using Singleton<Renderer3D>::destroy;
virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) override;
void drawModel(std::span<const Vertex> vertices, std::span<const uint32_t> indices, const TransformComponent& transform, std::shared_ptr<Texture> texture);
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;
@@ -211,8 +222,46 @@ private:
private:
// CPU element vertices
uint32_t* m_elementBufferBase { nullptr };
std::unique_ptr<uint32_t[]> m_elementBufferBase { nullptr };
uint32_t* m_elementBufferPtr { nullptr };
};
// -----------------------------------------
class RendererPostProcess final
: public Renderer2D
, public ruc::Singleton<RendererPostProcess> {
public:
using Singleton<RendererPostProcess>::s;
using Singleton<RendererPostProcess>::the;
using Singleton<RendererPostProcess>::destroy;
RendererPostProcess(s);
virtual ~RendererPostProcess();
void drawQuad(const TransformComponent& transform, std::shared_ptr<Texture> albedo, std::shared_ptr<Texture> position, std::shared_ptr<Texture> normal);
private:
virtual void loadShader() override;
};
// -----------------------------------------
class RendererLightCube final
: public RendererCubemap
, public ruc::Singleton<RendererLightCube> {
public:
using Singleton<RendererLightCube>::s;
using Singleton<RendererLightCube>::the;
using Singleton<RendererLightCube>::destroy;
RendererLightCube(s);
virtual ~RendererLightCube();
void beginScene(glm::mat4, glm::mat4) override {}
private:
virtual void loadShader() override;
};
} // namespace Inferno
@@ -0,0 +1,195 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstdint> // int32_t, uint8_t, uintt32_t
#include "glad/glad.h"
#include "glm/ext/matrix_float2x2.hpp" // glm::mat2
#include "glm/ext/matrix_float3x3.hpp" // glm::mat3
#include "glm/ext/vector_float4.hpp" // glm::vec4
#include "inferno/render/buffer.h"
#include "inferno/render/shader-storage-buffer.h"
namespace Inferno {
ShaderStorageBuffer::ShaderStorageBuffer(s)
{
// Get maximum uniformbuffer bindings the GPU supports
int32_t maxBindingPoints = 0;
glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxBindingPoints);
m_maxBindingPoints = static_cast<uint8_t>(maxBindingPoints);
}
ShaderStorageBuffer::~ShaderStorageBuffer()
{
}
// -----------------------------------------
// https://stackoverflow.com/questions/56512216#answer-56513136
void ShaderStorageBuffer::setLayout(std::string_view blockName, uint8_t bindingPoint, uint32_t shaderID)
{
VERIFY(bindingPoint < m_maxBindingPoints,
"shader storage buffer exceeded binding points: {}/{}", bindingPoint, m_maxBindingPoints);
if (!exists(blockName)) {
m_blocks[blockName.data()] = {};
}
BufferBlock& block = m_blocks[blockName.data()];
block.bindingPoint = bindingPoint;
block.memberOffsets.clear();
glBindBuffer(GL_SHADER_STORAGE_BUFFER, block.id);
// Get the shader block index
uint32_t resourceIndex = glGetProgramResourceIndex(shaderID, GL_SHADER_STORAGE_BLOCK, blockName.data());
VERIFY(resourceIndex != GL_INVALID_INDEX, "block doesnt exist in shader: {}::{}", blockName, shaderID);
// Get the amount of member variables
uint32_t prop = GL_NUM_ACTIVE_VARIABLES;
int32_t memberCount;
glGetProgramResourceiv(shaderID, GL_SHADER_STORAGE_BLOCK, resourceIndex, 1, &prop, 1, nullptr, &memberCount);
// Get the indices of the members
prop = GL_ACTIVE_VARIABLES;
std::vector<int32_t> members(memberCount);
glGetProgramResourceiv(shaderID, GL_SHADER_STORAGE_BLOCK, resourceIndex, 1, &prop, (int32_t)members.size(), nullptr, members.data());
// Reserve memory for the names of the members
int32_t memberNameSize;
glGetProgramInterfaceiv(shaderID, GL_BUFFER_VARIABLE, GL_MAX_NAME_LENGTH, &memberNameSize);
std::vector<char> memberNameBuffer(memberNameSize);
int32_t lastOffset = 0;
int32_t lastType = 0;
for (int32_t i = 0; i < memberCount; i++) {
// Get name of buffer variable
int32_t stringLength;
glGetProgramResourceName(shaderID, GL_BUFFER_VARIABLE, members[i], memberNameSize, &stringLength, memberNameBuffer.data());
std::string memberName = std::string(memberNameBuffer.begin(), memberNameBuffer.begin() + stringLength);
// Get the other data needed for computing
uint32_t props[7] = {
GL_OFFSET, // 0
GL_TYPE, // 1
GL_ARRAY_SIZE, // 2
GL_ARRAY_STRIDE, // 3
GL_MATRIX_STRIDE, // 4
GL_TOP_LEVEL_ARRAY_SIZE, // 5
GL_TOP_LEVEL_ARRAY_STRIDE, // 6
};
int32_t params[7];
glGetProgramResourceiv(shaderID, GL_BUFFER_VARIABLE, members[i], 7, props, 7, nullptr, params);
// ruc::error("{}", memberName);
// ruc::error("\n Offset: {}, type: {:#x}, arraySize: {}, arrayStride: {},\n matrixStride: {}, top level size: {}, top level stride: {}\n",
// params[0], params[1], params[2], params[3], params[4], params[5], params[6]);
// Array of structs
if (params[5] != 1 && params[6 != 0]) {
size_t bracketOpen = memberName.find_first_of('[');
size_t bracketClose = memberName.find_first_of(']');
std::string memberNameBegin = memberName.substr(0, bracketOpen); // name: myArray[0].member -> myArray
std::string memberNameMember = memberName.substr(bracketClose + 1); // name: myArray[0].member -> .member
for (int32_t j = 0; j < params[5]; ++j) {
lastOffset = params[0] + (j * params[6]); // calc offset
lastType = params[1];
// Only add the member variant the first time its encountered
if (j == 0 && block.memberOffsets.find(memberNameBegin) == block.memberOffsets.end()) {
block.memberOffsets.emplace(memberNameBegin, lastOffset); // name: myArray
}
// Only add the index variant the first time its encountered
std::string memberNameIndex = memberNameBegin + "[" + std::to_string(j) + "]"; // name: myArray -> myArray[i]
if (block.memberOffsets.find(memberNameIndex) == block.memberOffsets.end()) {
block.memberOffsets.emplace(memberNameIndex, lastOffset);
}
block.memberOffsets.emplace(memberNameIndex + memberNameMember, // name: myArray -> myArray[i].member
lastOffset);
}
}
// Array of primitives
else if (params[2] != 1 && params[3] != 0) {
std::string memberNameBegin = memberName.substr(0, memberName.find_first_of('[')); // name: myArray[0] -> myArray
for (int32_t j = 0; j < params[2]; ++j) {
lastOffset = params[0] + (j * params[3]); // calc offset
lastType = params[1];
// Only add the member variant the first time its encountered
if (j == 0) {
block.memberOffsets.emplace(memberNameBegin, lastOffset); // name: myArray
}
block.memberOffsets.emplace(memberNameBegin + "[" + std::to_string(j) + "]", // name: myArray -> myArray[i]
lastOffset);
}
}
// Matrix case
else if (params[4] != 0) {
lastType = params[1];
lastOffset = params[0];
block.memberOffsets.emplace(memberName, params[0]);
}
else {
lastType = params[1];
lastOffset = params[0];
block.memberOffsets.emplace(memberName, params[0]);
}
}
block.size = lastOffset + BufferElement::getGLTypeSize(lastType);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
// Results
// for (auto [k, v] : block.memberOffsets) {
// ruc::error("{}:{}", k, v);
// }
}
void ShaderStorageBuffer::create(std::string_view blockName)
{
VERIFY(exists(blockName), "shader storage buffer block doesnt exist");
BufferBlock& block = m_blocks[blockName.data()];
if (block.id != 0) {
glDeleteBuffers(1, &block.id);
}
// Allocate buffer
block.id = UINT_MAX;
glGenBuffers(1, &block.id);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, block.id);
glBufferData(GL_SHADER_STORAGE_BUFFER, block.size, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
// Bind buffer to binding point
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, block.bindingPoint, block.id);
}
void ShaderStorageBuffer::setValue(std::string_view blockName, std::string_view member, bool value)
{
setValue(blockName, member, static_cast<uint32_t>(value), sizeof(uint32_t));
}
void ShaderStorageBuffer::setValue(std::string_view blockName, std::string_view member, glm::mat2 value)
{
setValue(blockName, member, static_cast<glm::mat4>(value), sizeof(glm::vec4) * 2);
}
void ShaderStorageBuffer::setValue(std::string_view blockName, std::string_view member, glm::mat3 value)
{
setValue(blockName, member, static_cast<glm::mat4>(value), sizeof(glm::vec4) * 3);
}
} // namespace Inferno
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstddef> // size_t
#include <cstdint> // uint8_t, uint32_t
#include <string_view>
#include <unordered_map>
#include "glad/glad.h"
#include "glm/ext/matrix_float2x2.hpp" // glm::mat2
#include "glm/ext/matrix_float3x3.hpp" // glm::mat3
#include "ruc/singleton.h"
#define CHECK_SET_CALL(blockName, member) \
VERIFY(exists(blockName), "shader storage buffer block doesnt exist: {}", blockName); \
const BufferBlock& block = m_blocks[blockName.data()]; \
VERIFY(block.memberOffsets.find(member.data()) != block.memberOffsets.end(), \
"shader storage buffer member doesnt exist: {}", member);
namespace Inferno {
struct BufferBlock {
uint32_t id { 0 };
uint32_t size { 0 };
uint8_t bindingPoint { 0 };
std::unordered_map<std::string, uint32_t> memberOffsets {};
};
class ShaderStorageBuffer final : public ruc::Singleton<ShaderStorageBuffer> { // Shader Storage Buffer Object, SSBO
public:
ShaderStorageBuffer(s);
virtual ~ShaderStorageBuffer();
void setLayout(std::string_view blockName, uint8_t bindingPoint, uint32_t shaderID);
void create(std::string_view blockName);
bool exists(std::string_view blockName) const { return m_blocks.find(blockName.data()) != m_blocks.end(); }
template<typename T> // Capture value by reference, instead of decaying to pointer
void setValue(std::string_view blockName, std::string_view member, T&& value, size_t size = 0)
{
CHECK_SET_CALL(blockName, member);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, block.id);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, block.memberOffsets.at(member.data()), (size) ? size : sizeof(T), &value);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}
// Exceptions:
void setValue(std::string_view blockName, std::string_view member, bool value);
void setValue(std::string_view blockName, std::string_view member, glm::mat2 value);
void setValue(std::string_view blockName, std::string_view member, glm::mat3 value);
private:
uint8_t m_maxBindingPoints { 0 };
std::unordered_map<std::string, BufferBlock> m_blocks;
};
} // namespace Inferno
+24
View File
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "glm/ext/vector_float3.hpp" // glm::vec3
namespace Inferno {
// Shader storage block layouts, using std430 memory layout rules
#define MAX_DIRECTIONAL_LIGHTS 4
struct alignas(16) DirectionalLightBlock {
alignas(16) glm::vec3 direction { 0 };
alignas(16) glm::vec3 ambient { 0 };
alignas(16) glm::vec3 diffuse { 0 };
alignas(16) glm::vec3 specular { 0 };
};
} // namespace Inferno
+241
View File
@@ -0,0 +1,241 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint32_t, uint8_t
#include <string_view>
#include "glad/glad.h"
#include "glm/ext/matrix_float2x2.hpp" // glm::mat2
#include "glm/ext/matrix_float3x3.hpp" // glm::mat3
#include "glm/gtc/type_ptr.hpp" // glm::value_ptr
#include "ruc/meta/assert.h"
#include "inferno/render/buffer.h"
#include "inferno/render/uniformbuffer.h"
namespace Inferno {
Uniformbuffer::Uniformbuffer(s)
{
// Get maximum uniformbuffer bindings the GPU supports
int32_t maxBindingPoints = 0;
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxBindingPoints);
m_maxBindingPoints = static_cast<uint8_t>(maxBindingPoints);
}
Uniformbuffer::~Uniformbuffer()
{
for (const auto& [_, block] : m_blocks) {
glDeleteBuffers(1, &block.id);
}
m_blocks.clear();
}
// -----------------------------------------
void Uniformbuffer::setLayout(std::string_view blockName, const UniformbufferBlock& block)
{
VERIFY(block.size && block.bindingPoint && block.uniformLocations.size(),
"invalid uniformbuffer block definition: {}", blockName);
VERIFY(block.bindingPoint < m_maxBindingPoints,
"uniformbuffer exceeded binding points: {}/{}", block.bindingPoint, m_maxBindingPoints);
m_blocks[blockName] = block;
}
void Uniformbuffer::setLayout(std::string_view blockName, uint8_t bindingPoint, const BufferLayout& layout)
{
VERIFY(bindingPoint < m_maxBindingPoints,
"uniformbuffer exceeded binding points: {}/{}", bindingPoint, m_maxBindingPoints);
if (!exists(blockName)) {
m_blocks[blockName] = {};
}
UniformbufferBlock& block = m_blocks[blockName];
block.bindingPoint = bindingPoint;
block.uniformLocations.clear();
// Example block layout:
// - mat3
// - float
// - vec2
// - vec2
// - float
// Chunks, 4 slots, 4 bytes per slot
// [x][x][x][ ] #1
// [x][x][x][ ] #2
// [x][x][x][ ] #3
// [x][ ][x][x] #4
// [x][x][x][ ] #5
size_t chunk = 0;
uint8_t offset = 0;
for (auto it = layout.begin(); it != layout.end(); ++it) {
BufferElementType type = it->type();
const std::string& name = it->name();
// Calculate offset
switch (type) {
// Scalar 1
case BufferElementType::Bool:
case BufferElementType::Int:
case BufferElementType::Uint:
case BufferElementType::Float: {
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Jump
offset += 1;
break;
}
// Scalar 2
case BufferElementType::Bool2:
case BufferElementType::Int2:
case BufferElementType::Uint2:
case BufferElementType::Vec2: {
// Add padding
if (offset == 1) {
offset++;
}
if (offset == 3) {
offset = 0;
chunk++;
}
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Jump
offset += 2;
break;
}
// Scalar 3
case BufferElementType::Bool3:
case BufferElementType::Int3:
case BufferElementType::Uint3:
case BufferElementType::Vec3: {
// Add padding
if (offset != 0) {
offset = 0;
chunk++;
}
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Jump
offset += 3;
break;
}
// Scalar 4
case BufferElementType::Bool4:
case BufferElementType::Int4:
case BufferElementType::Uint4:
case BufferElementType::Vec4: {
// Add padding
if (offset != 0) {
offset = 0;
chunk++;
}
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Jump
offset += 4;
break;
}
// Array types
case BufferElementType::Mat2:
case BufferElementType::Mat3:
case BufferElementType::Mat4: {
// Add padding
if (offset != 0) {
offset = 0;
chunk++;
}
// Offset
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
// Additional rows
if (type == BufferElementType::Mat2) {
chunk += 1;
}
else if (type == BufferElementType::Mat3) {
chunk += 2;
}
else {
chunk += 3;
}
// Jump
offset += 4;
break;
}
// TODO: Implement these types
case BufferElementType::Double:
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
case BufferElementType::MatDouble2:
case BufferElementType::MatDouble3:
case BufferElementType::MatDouble4:
VERIFY_NOT_REACHED();
case BufferElementType::None:
VERIFY_NOT_REACHED();
};
// Overflow slots to next chunk
if (offset > 3) {
offset = 0;
chunk++;
}
}
// Pad the end of the buffer
if (offset != 0) {
offset = 0;
chunk++;
}
block.size = chunk * 16;
}
void Uniformbuffer::create(std::string_view blockName)
{
VERIFY(exists(blockName), "uniformbuffer block doesnt exist");
UniformbufferBlock& block = m_blocks[blockName];
if (block.id != 0) {
glDeleteBuffers(1, &block.id);
}
// Allocate buffer
block.id = UINT_MAX;
glGenBuffers(1, &block.id);
glBindBuffer(GL_UNIFORM_BUFFER, block.id);
glBufferData(GL_UNIFORM_BUFFER, block.size, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// Bind buffer to binding point
glBindBufferBase(GL_UNIFORM_BUFFER, block.bindingPoint, block.id);
}
void Uniformbuffer::setValue(std::string_view blockName, std::string_view member, bool value)
{
setValue(blockName, member, static_cast<uint32_t>(value), sizeof(uint32_t));
}
void Uniformbuffer::setValue(std::string_view blockName, std::string_view member, glm::mat2 value)
{
setValue(blockName, member, static_cast<glm::mat4>(value), sizeof(glm::vec4) * 2);
}
void Uniformbuffer::setValue(std::string_view blockName, std::string_view member, glm::mat3 value)
{
setValue(blockName, member, static_cast<glm::mat4>(value), sizeof(glm::vec4) * 3);
}
} // namespace Inferno
+114
View File
@@ -0,0 +1,114 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstddef> // size_t
#include <cstdint> // uint8_t, uint32_t
#include <string>
#include <string_view>
#include <unordered_map>
#include "glad/glad.h"
#include "glm/ext/matrix_float2x2.hpp" // glm::mat2
#include "glm/ext/matrix_float3x3.hpp" // glm::mat3
#include "ruc/singleton.h"
#include "inferno/render/buffer.h"
#define CHECK_UNIFORM_SET_CALL(blockName, member) \
VERIFY(exists(blockName), "uniformbuffer block doesnt exist: {}", blockName); \
const UniformbufferBlock& block = m_blocks[blockName]; \
VERIFY(block.uniformLocations.find(member.data()) != block.uniformLocations.end(), \
"uniformbuffer member doesnt exist: {}", member);
namespace Inferno {
struct UniformbufferBlock {
uint32_t id { 0 };
uint32_t size { 0 };
uint8_t bindingPoint { 0 };
std::unordered_map<std::string, uint32_t> uniformLocations {};
};
class Uniformbuffer final : public ruc::Singleton<Uniformbuffer> { // Uniform Buffer Object, UBO
public:
Uniformbuffer(s);
~Uniformbuffer();
void setLayout(std::string_view blockName, const UniformbufferBlock& block);
void setLayout(std::string_view blockName, uint8_t bindingPoint, const BufferLayout& layout);
void create(std::string_view blockName);
template<typename T> // Capture value by reference, instead of decaying to pointer
void setValue(std::string_view blockName, std::string_view member, T&& value, size_t size = 0)
{
CHECK_UNIFORM_SET_CALL(blockName, member);
glBindBuffer(GL_UNIFORM_BUFFER, block.id);
glBufferSubData(GL_UNIFORM_BUFFER, block.uniformLocations.at(member.data()), (size) ? size : sizeof(T), &value);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
// Exceptions:
void setValue(std::string_view blockName, std::string_view member, bool value);
void setValue(std::string_view blockName, std::string_view member, glm::mat2 value);
void setValue(std::string_view blockName, std::string_view member, glm::mat3 value);
bool exists(std::string_view blockName) const { return m_blocks.find(blockName) != m_blocks.end(); }
private:
uint8_t m_maxBindingPoints { 0 };
std::unordered_map<std::string_view, UniformbufferBlock> m_blocks;
};
} // namespace Inferno
#if 0
// -----------------------------------------
// Example usage:
Uniformbuffer::the().setLayout(
"ExampleBlock", 0,
{
{ BufferElementType::Mat3, "a" },
{ BufferElementType::Float, "b" },
{ BufferElementType::Vec2, "c" },
{ BufferElementType::Vec2, "d" },
{ BufferElementType::Float, "e" },
});
Uniformbuffer::the().create("ExampleBlock");
#endif
// -----------------------------------------
// Memory alignment of uniform blocks using std140
//
// Main points:
// - Memory is organized into chunks.
// - A block is at least the size of 1 chunk.
// - One chunk has 4 slots, 4 bytes per slot.
// - Can't fit? Move to next chunk.
//
// The rules:
// 1. Scalar (bool, int, uint, float) takes up 1 slot, can appear after anything
// 2. Vec2 takes up 2 slots, in first or last half of a chunk
// 3. Vec3 takes up 3 slots, only at the start of a chunk
// 4. Everything else:
// - Take up maxumum room
// - As often as needed
// - Add padding as needed
// 5. Mat3 (or any matrix) are treated like arrays
// 6. Each member of *any* array gets its own chunk
// TODO: How do double types work?
// -----------------------------------------
// References:
// - https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL
// - https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout
// - https://www.oreilly.com/library/view/opengl-programming-guide/9780132748445/app09lev1sec2.html (The std140 Layout Rules)
// - https://www.youtube.com/watch?v=JPvbRko9lBg (WebGL 2: Uniform Buffer Objects)
+3 -2
View File
@@ -9,7 +9,7 @@
#include <limits> // std::numeric_limits
#include <utility> // std::pair
#include "entt/entity/entity.hpp" // ent::entity
#include "entt/entity/fwd.hpp" // ent::entity
#include "ruc/file.h"
#include "ruc/format/log.h"
#include "ruc/json/json.h"
@@ -25,6 +25,8 @@
#include "inferno/component/tagcomponent.h"
#include "inferno/component/textareacomponent.h"
#include "inferno/component/transformcomponent.h"
#include "inferno/render/renderer.h"
#include "inferno/render/uniformbuffer.h"
#include "inferno/scene/scene.h"
#include "inferno/script/nativescript.h"
#include "inferno/system/camerasystem.h"
@@ -84,7 +86,6 @@ void Scene::update(float deltaTime)
void Scene::render()
{
RenderSystem::the().render();
TextAreaSystem::the().render();
}
void Scene::destroy()
+14 -1
View File
@@ -53,10 +53,23 @@ std::pair<glm::mat4, glm::mat4> CameraSystem::projectionView()
}
VERIFY_NOT_REACHED();
return {};
}
glm::vec3 CameraSystem::translate()
{
auto view = m_registry->view<TransformComponent, CameraComponent>();
for (auto [entity, transform, camera] : view.each()) {
return transform.translate;
}
VERIFY_NOT_REACHED();
return {};
}
// -----------------------------------------
void CameraSystem::updateOrthographic(TransformComponent& transform, CameraComponent& camera)
{
// Update camera matrix
+1
View File
@@ -32,6 +32,7 @@ public:
* @brief Return a pair from the camera component: { projection, view }
*/
std::pair<glm::mat4, glm::mat4> projectionView();
glm::vec3 translate();
void setRegistry(std::shared_ptr<entt::registry> registry) { m_registry = registry; };
+167 -11
View File
@@ -4,41 +4,148 @@
* SPDX-License-Identifier: MIT
*/
#include "glm/ext/matrix_transform.hpp" // glm::translate, glm::rotate, glm::scale, glm::radians
#include "inferno/component/model-component.h"
#include <cstdint> // int32_t
#include "glad/glad.h"
#include "ruc/format/log.h"
#include "inferno/component/cubemap-component.h"
#include "inferno/component/model-component.h"
#include "inferno/component/spritecomponent.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/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/textareasystem.h"
namespace Inferno {
RenderSystem::RenderSystem(s)
{
ruc::info("RenderSystem initialized");
}
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()
{
auto quadView = m_registry->view<TransformComponent, SpriteComponent>();
static constexpr TransformComponent transformIdentity;
for (auto [entity, transform, sprite] : quadView.each()) {
Renderer2D::the().drawQuad(transform, sprite.color, sprite.texture);
}
// ---------------------------------
// Deferred rendering to a framebuffer
auto cubemapView = m_registry->view<TransformComponent, CubemapComponent>();
framebufferSetup(m_framebuffer);
for (auto [entity, transform, cubemap] : cubemapView.each()) {
RendererCubemap::the().drawCubemap(transform, cubemap.color, cubemap.texture);
}
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>();
@@ -46,8 +153,57 @@ void RenderSystem::render()
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>();
for (auto [entity, transform, sprite] : quadView.each()) {
Renderer2D::the().drawQuad(transform, sprite.color, sprite.texture);
}
Renderer2D::the().endScene();
TextAreaSystem::the().render();
RendererFont::the().endScene();
}
} // namespace Inferno
+18 -4
View File
@@ -6,25 +6,39 @@
#pragma once
#include <memory> //std::shared_ptr
#include <cstdint> // int32_t, uint32_t
#include <memory> //std::shared_ptr
#include "entt/entity/fwd.hpp" // entt::registry
#include "entt/entity/registry.hpp" // entt::entity, entt::registry
#include "ruc/singleton.h"
#include "inferno/render/renderer.h"
namespace Inferno {
class Framebuffer;
class RenderSystem final : public ruc::Singleton<RenderSystem> {
public:
RenderSystem(s);
virtual ~RenderSystem();
void initialize(uint32_t width, uint32_t height);
void render();
void resize(int32_t width, int32_t height);
void setRegistry(std::shared_ptr<entt::registry> registry) { m_registry = registry; };
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;
};
+1 -1
View File
@@ -18,7 +18,7 @@
namespace Inferno {
using Symbols = std::vector<std::shared_ptr<Symbol>>;
using SymbolQuad = std::array<SymbolVertex, Renderer<void>::vertexPerQuad>;
using SymbolQuad = std::array<SymbolVertex, RendererFont::vertexPerQuad>;
class Scene;
class TextAreaComponent;
+3 -3
View File
@@ -122,17 +122,17 @@ void Window::initialize()
switch (action) {
case GLFW_PRESS: {
KeyPressEvent event(key);
KeyPressEvent event(key, mods);
w.m_eventCallback(event);
break;
}
case GLFW_RELEASE: {
KeyReleaseEvent event(key);
KeyReleaseEvent event(key, mods);
w.m_eventCallback(event);
break;
}
case GLFW_REPEAT: {
KeyRepeatEvent event(key);
KeyRepeatEvent event(key, mods);
w.m_eventCallback(event);
break;
}
+1
View File
@@ -33,3 +33,4 @@ target_include_directories(${ENGINE}-dependencies PUBLIC
"glad/include"
"lua")
target_link_libraries(${ENGINE}-dependencies glfw ruc ruc-test assimp)
target_compile_options(${ENGINE}-dependencies PRIVATE ${COMPILE_FLAGS_DEPS})
Vendored
+1 -1
Submodule vendor/ruc updated: 8520952235...d5bf50715e