Compare commits

...
26 Commits
Author SHA1 Message Date
Riyyi cc33b5d481 Render+Asset: Fix derived Renderer2D, fix Skybox uniform 2024-08-31 19:54:23 +02:00
Riyyi 67bb6a5671 Render: Fix release build via out-of-line destructors 2024-08-31 00:29:03 +02:00
Riyyi 3ff8838e01 Render: Fix memory leak, use after free :^) 2024-08-30 23:02:28 +02:00
Riyyi 3ae6ecae8a Meta: Enable ASan and UBSan, disable warnings in vendor directory
(address sanitizer, undefined behavior sanitizer)
2024-08-29 22:15:13 +02:00
Riyyi 6b6c588378 Component: Put JSON serialization functions in a separate file 2024-08-25 21:10:03 +02:00
Riyyi 9145f90c1d Meta: Update ruc library 2024-08-25 13:26:04 +02:00
Riyyi b9043dbb4b Render: Add and implement shader storage buffer (SSBO) 2024-08-24 20:14:06 +02:00
Riyyi b6e68eccec Doc: Add GLSL std430 memory layout documentation 2024-08-20 21:31:01 +02:00
Riyyi d16fade497 Everywhere: Work towards lighting support 2024-08-19 17:56:57 +02:00
Riyyi b8e4d9ef3f Render: Allow arbitrary data in uniformbuffer objects 2024-08-19 17:41:31 +02:00
Riyyi 03e8210165 Event+Asset+Keycodes: Add keybind for taking screenshots 2024-08-17 21:05:43 +02:00
Riyyi dcf2a85208 Asset+Component+Render: Render quads in the world as 3D objects 2024-08-11 22:06:55 +02:00
Riyyi b2ed951b1e Render: Add more value setter types for uniformbuffer objects 2024-08-11 22:06:55 +02:00
Riyyi d63dfe2f9f Render: Update getter to match naming convention 2024-08-10 22:05:21 +02:00
Riyyi faacdca424 App: Print average frame-time on exit 2024-08-10 21:58:50 +02:00
Riyyi b5f3cae5ba Render+Scene: Implement uniformbuffer objects 2024-08-10 21:58:50 +02:00
Riyyi 1d5f5a1ad8 Shader: Cache uniform location lookup 2024-08-09 23:10:39 +02:00
Riyyi 3cd2fab637 Render: Add support for int/double shader attributes 2024-08-07 21:52:13 +02:00
Riyyi c5ed219ad2 Render: Dont create double framebuffers 2024-08-05 19:26:38 +02:00
Riyyi 4dbac7ee38 Render: Make Framebuffer class more flexible 2024-08-05 19:24:38 +02:00
Riyyi a5b7a49447 Render: Make Framebuffer class more flexible 2024-08-05 19:12:39 +02:00
Riyyi fd72f6610e Render: Change RenderCommand::clearBit function 2024-08-05 15:19:16 +02:00
Riyyi 21319b93ad Render: Cleanup buffer 2024-08-05 11:24:52 +02:00
Riyyi cd36841039 Asset+Render+App: Implement render to framebuffer 2024-08-04 20:54:57 +02:00
Riyyi 538d0b5ce7 Asset+Render: Add Framebuffers 2024-08-03 22:41:23 +02:00
Riyyi 5f6a5f48dd Asset+Component+Render: Fix assimp modeling loading + batching 2024-08-02 02:08:12 +02:00
70 changed files with 2666 additions and 1099 deletions
+19 -7
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")
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
+14 -9
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"
}
},
@@ -44,12 +45,13 @@
"id": { "id": 97897897 },
"tag": { "tag": "Quad 2" },
"transform" : {
"translate": [ 1.1, 0.0, 0.0 ],
"translate": [ 2.5, 0.0, 0.0 ],
"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"
}
},
@@ -57,12 +59,13 @@
"id": { "id": 3424242 },
"tag": { "tag": "Quad 3" },
"transform" : {
"translate": [ 2.2, 1.0, 0.0 ],
"translate": [ 5.0, 1.0, 0.0 ],
"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": [
@@ -70,12 +73,13 @@
"id": { "id": 4345472 },
"tag": { "tag": "Quad 4" },
"transform" : {
"translate": [ 0.85, 0.0, 0.0 ],
"translate": [ 1.7, 0.0, 0.0 ],
"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": [
@@ -83,12 +87,13 @@
"id": { "id": 5234723 },
"tag": { "tag": "Quad 5" },
"transform" : {
"translate": [ 1.0, 0.0, 0.0 ],
"translate": [ 2.0, 0.0, 0.0 ],
"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})
# ------------------------------------------
+26 -23
View File
@@ -6,22 +6,25 @@
#include <utility> // std::pair
#include "glm/ext/vector_float3.hpp"
#include "glm/gtc/type_ptr.hpp" // glm::make_mat4
#include "ruc/format/log.h"
#include "ruc/meta/assert.h"
#include "inferno/application.h"
#include "inferno/component/transformcomponent.h"
#include "inferno/core.h"
#include "inferno/event/applicationevent.h"
#include "inferno/event/event.h"
#include "inferno/event/keyevent.h"
#include "inferno/event/mouseevent.h"
// #include "inferno/io/gltffile.h"
#include "inferno/asset/font.h"
#include "inferno/io/input.h"
#include "inferno/keycodes.h"
#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"
@@ -51,12 +54,11 @@ Application::Application()
Input::initialize();
RenderCommand::initialize();
RenderSystem::the().initialize(m_window->getWidth(), m_window->getHeight());
m_scene = std::make_shared<Scene>();
m_scene->initialize();
// Load assets
// m_font = FontManager::the().load("assets/fnt/dejavu-sans");
// auto bla = GlTFFile::read("assets/gltf/box.glb");
@@ -77,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();
@@ -145,13 +150,20 @@ int Application::run()
// offset
#endif
double gametime = 0;
uint64_t frames = 0;
while (!m_window->shouldClose()) {
float time = Time::time();
float deltaTime = time - m_lastFrameTime;
m_lastFrameTime = time;
// ruc::debug("Frametime " << deltaTime * 1000 << "ms");
// ruc::debug("Frametime {}ms", deltaTime * 1000);
gametime += deltaTime;
frames++;
// ---------------------------------
// Update
update();
@@ -160,32 +172,18 @@ int Application::run()
m_window->update();
m_scene->update(deltaTime);
// ---------------------------------
// Render
render();
RenderCommand::clearColor({ 0.2f, 0.3f, 0.3f, 1.0f });
RenderCommand::clear();
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_window->render();
}
ruc::debug("Application shutdown");
ruc::debug("Average frametime: {:.2f}ms", (gametime / frames) * 1000);
return m_status;
}
@@ -214,7 +212,7 @@ bool Application::onWindowResize(WindowResizeEvent& e)
{
ruc::info("WindowResizeEvent {}x{}", e.getWidth(), e.getHeight());
RenderCommand::setViewport(0, 0, e.getWidth(), e.getHeight());
RenderSystem::the().resize(e.getWidth(), e.getHeight());
return true;
}
@@ -228,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;
}
+3 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
@@ -8,18 +8,18 @@
#include <memory> // std::unique_ptr, std::shared_ptr
#include "ruc/singleton.h"
namespace Inferno {
class Event;
class Font;
class Framebuffer;
class KeyPressEvent;
class MousePositionEvent;
class Scene;
class Window;
class WindowCloseEvent;
class WindowResizeEvent;
struct TransformComponent;
class Application {
public:
+6 -2
View File
@@ -38,18 +38,22 @@ bool AssetManager::exists(std::string_view path)
return m_assetList.find(path.data()) != m_assetList.end();
}
void AssetManager::remove(std::string_view path)
std::nullptr_t AssetManager::remove(std::string_view path)
{
if (exists(path)) {
m_assetList.erase(path.data());
}
return nullptr;
}
void AssetManager::remove(std::shared_ptr<Asset> asset)
std::nullptr_t AssetManager::remove(std::shared_ptr<Asset> asset)
{
if (exists(asset->path())) {
m_assetList.erase(asset->path());
}
return nullptr;
}
} // namespace Inferno
+8 -5
View File
@@ -6,10 +6,12 @@
#pragma once
#include <cstddef> // std::nullptr_t
#include <memory> // std::shared_ptr
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility> // std::forward
#include "ruc/meta/assert.h"
#include "ruc/meta/types.h"
@@ -36,6 +38,7 @@ public:
virtual bool isTexture() const { return false; }
virtual bool isTexture2D() const { return false; }
virtual bool isTextureCubemap() const { return false; }
virtual bool isTextureFramebuffer() const { return false; }
protected:
Asset(std::string_view path)
@@ -61,8 +64,8 @@ public:
void add(std::string_view path, std::shared_ptr<Asset> asset);
bool exists(std::string_view path);
void remove(std::string_view path);
void remove(std::shared_ptr<Asset> asset);
std::nullptr_t remove(std::string_view path);
std::nullptr_t remove(std::shared_ptr<Asset> asset);
template<IsAsset T>
std::shared_ptr<T> get(std::string_view path)
@@ -76,14 +79,14 @@ public:
return std::static_pointer_cast<T>(asset);
}
template<IsAsset T>
std::shared_ptr<T> load(std::string_view path)
template<IsAsset T, typename... Args>
std::shared_ptr<T> load(std::string_view path, Args&&... args)
{
if (exists(path)) {
return get<T>(path);
}
auto asset = T::create(path);
auto asset = T::create(path, std::forward<Args>(args)...);
add(path, asset);
return asset;
}
+22 -22
View File
@@ -8,10 +8,10 @@
#include <memory> // std::shared_ptr
#include "assimp/Importer.hpp"
#include "assimp/mesh.h"
#include "assimp/postprocess.h"
#include "assimp/scene.h"
#include "assimp/texture.h"
#include "ruc/format/log.h"
#include "inferno/asset/model.h"
#include "inferno/asset/texture.h"
@@ -23,6 +23,7 @@ std::shared_ptr<Model> Model::create(std::string_view path)
auto result = std::shared_ptr<Model>(new Model(path));
Assimp::Importer importer; // importer destructor uses RAII cleanup
importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE);
const aiScene* scene = importer.ReadFile(
path.data(),
aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType);
@@ -39,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];
@@ -51,7 +52,7 @@ void Model::processNode(std::shared_ptr<Model> model, aiNode* node, const aiScen
{
for (uint32_t i = 0; i < node->mNumMeshes; ++i) {
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
processMesh(model, mesh, scene);
processMesh(model, mesh, scene, node->mTransformation);
}
for (uint32_t i = 0; i < node->mNumChildren; ++i) {
@@ -59,52 +60,51 @@ void Model::processNode(std::shared_ptr<Model> model, aiNode* node, const aiScen
}
}
void Model::processMesh(std::shared_ptr<Model> model, aiMesh* mesh, const aiScene* scene)
void Model::processMesh(std::shared_ptr<Model> model, aiMesh* mesh, const aiScene* scene, aiMatrix4x4 parentTransform)
{
VERIFY(mesh->HasPositions(), "malformed model");
VERIFY(mesh->HasNormals(), "malformed model");
// Pre-allocate memory
model->m_vertices = std::vector<Vertex>(mesh->mNumVertices);
size_t startIndex = model->m_vertices.size();
model->m_vertices.resize(startIndex + mesh->mNumVertices);
for (uint32_t i = 0; i < mesh->mNumVertices; ++i) {
aiVector3D v = mesh->mVertices[i];
model->m_vertices[i].position = { v.x, v.y, v.z };
aiVector3D v = parentTransform * mesh->mVertices[i];
model->m_vertices[startIndex + i].position = { v.x, v.y, v.z };
}
// Size of vertices == size of normals
for (uint32_t i = 0; i < mesh->mNumVertices; ++i) {
aiVector3D normal = mesh->mNormals[i];
model->m_vertices[i].normal = { normal.x, normal.y, normal.x };
aiVector3D normal = mesh->mNormals[startIndex + i];
model->m_vertices[startIndex + i].normal = { normal.x, normal.y, normal.z };
}
if (mesh->HasTextureCoords(0)) {
// Size of vertices == size of texture coordinates
for (uint32_t i = 0; i < mesh->mNumVertices; ++i) {
aiVector3D tc = mesh->mTextureCoords[0][i];
model->m_vertices[i].textureCoordinates = { tc.x, tc.y };
model->m_vertices[startIndex + i].textureCoordinates = { tc.x, tc.y };
}
}
// TODO: position in the texture atlas
if (mesh->HasFaces()) {
// Pre-allocate memory
size_t startIndex2 = model->m_elements.size();
model->m_elements.resize(startIndex2 + (mesh->mNumFaces * 3)); // assuming triangles
size_t offset = 0;
for (uint32_t i = 0; i < mesh->mNumFaces; ++i) {
aiFace face = mesh->mFaces[i];
for (uint32_t j = 0; j < face.mNumIndices; ++j) {
model->m_elements.push_back(face.mIndices[j]);
VERIFY(face.mNumIndices == 3, "unsupported face type: {}", face.mNumIndices);
for (uint32_t j = 0; j < face.mNumIndices; ++j, ++offset) {
// Indices are referenced relative to vertices[0], if there are multiple meshes,
// then the indices need to be offset by the total amount of vertices
model->m_elements[startIndex2 + (i * 3) + j] = startIndex + face.mIndices[j];
}
}
}
ruc::debug("mesh: {:p}", mesh->mTextureCoordsNames);
// for (size_t i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
// ruc::debug("mesh: {}", mesh->mTextureCoordsNames[i]);
// }
ruc::debug("has texture: {}", scene->HasTextures());
ruc::error("asset::model vert {}", model->m_vertices.size());
ruc::error("asset::model elem {}", model->m_elements.size());
}
} // namespace Inferno
+8 -1
View File
@@ -39,7 +39,7 @@ private:
static void processScene(std::shared_ptr<Model> model, const aiScene* scene);
static void processNode(std::shared_ptr<Model> model, aiNode* node, const aiScene* scene);
static void processMesh(std::shared_ptr<Model> model, aiMesh* mesh, const aiScene* scene);
static void processMesh(std::shared_ptr<Model> model, aiMesh* mesh, const aiScene* scene, aiMatrix4x4 parentTransform = aiMatrix4x4());
virtual bool isModel() const override { return true; }
@@ -50,4 +50,11 @@ private:
std::shared_ptr<Texture2D> m_texture;
};
// clang-format off
template<>
inline bool Asset::fastIs<Model>() const { return isModel(); }
// clang-format on
} // namespace Inferno
// TODO: figure out this weird thing: https://github.com/assimp/assimp/blob/master/BUILDBINARIES_EXAMPLE.bat#L6-L10
+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;
};
// -----------------------------------------
+123 -10
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,13 +25,66 @@ Texture::~Texture()
glDeleteTextures(1, &m_id);
}
// -----------------------------------------
void Texture::savePNG(std::string_view path, std::shared_ptr<Texture> texture)
{
texture->bind();
// Allocate memory
uint32_t dataFormat = texture->m_dataFormat == GL_RGBA ? 4 : 3;
size_t dataSize = texture->m_width * texture->m_height * dataFormat;
std::vector<unsigned char> data(dataSize);
// Read texture data from the GPU
glGetTexImage(GL_TEXTURE_2D, 0, texture->m_dataFormat, texture->m_dataType, data.data());
// Write image to a file
stbi_flip_vertically_on_write(1);
stbi_write_png(
path.data(),
texture->m_width,
texture->m_height,
dataFormat,
data.data(),
texture->m_width * dataFormat);
texture->unbind();
}
void Texture::saveScreenshotPNG(std::string_view path, uint32_t width, uint32_t height)
{
// Allocate memory
size_t dataSize = width * height * 4;
std::vector<unsigned char> data(dataSize);
// Set the pack alignment to 1 (to avoid row alignment issues)
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Read the pixels from the default framebuffer
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
stbi_flip_vertically_on_write(1);
stbi_write_png(path.data(), width, height, 4, data.data(), width * 4);
}
// -----------------------------------------
void Texture::init(uint32_t width, uint32_t height, uint8_t channels)
{
init(width, height,
(channels == 3) ? GL_RGB8 : GL_RGBA8,
(channels == 3) ? GL_RGB : GL_RGBA,
GL_UNSIGNED_BYTE);
}
void Texture::init(uint32_t width, uint32_t height, uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType)
{
m_width = width;
m_height = height;
m_internalFormat = (channels == 3) ? GL_RGB8 : GL_RGBA8;
m_dataFormat = (channels == 3) ? GL_RGB : GL_RGBA;
m_internalFormat = internalFormat;
m_dataFormat = dataFormat;
m_dataType = dataType;
}
// -----------------------------------------
@@ -49,7 +104,7 @@ std::shared_ptr<Texture2D> Texture2D::create(std::string_view path)
VERIFY(data, "failed to load image: '{}'", path);
result->init(width, height, channels);
result->create(data);
result->createImpl(data);
// Clean resources
stbi_image_free(data);
@@ -78,7 +133,7 @@ std::shared_ptr<Texture2D> Texture2D::create(aiTexture* texture)
}
result->init(width, height, channels);
result->create(data);
result->createImpl(data);
return result;
}
@@ -99,7 +154,7 @@ void Texture2D::unbind() const
glBindTexture(GL_TEXTURE_2D, 0);
}
void Texture2D::create(unsigned char* data)
void Texture2D::createImpl(unsigned char* data)
{
m_id = UINT_MAX;
@@ -121,7 +176,7 @@ void Texture2D::create(unsigned char* data)
m_width, m_height, // Image width/height
0, // Always 0 (legacy)
m_dataFormat, // Texture source format
GL_UNSIGNED_BYTE, // Texture source datatype
m_dataType, // Texture source datatype
data); // Image data
// Set the texture wrapping / filtering options
@@ -143,7 +198,7 @@ std::shared_ptr<TextureCubemap> TextureCubemap::create(std::string_view path)
{
auto result = std::shared_ptr<TextureCubemap>(new TextureCubemap(path));
result->create();
result->createImpl();
return result;
}
@@ -164,7 +219,7 @@ void TextureCubemap::unbind() const
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
void TextureCubemap::create()
void TextureCubemap::createImpl()
{
m_id = UINT_MAX;
@@ -206,7 +261,7 @@ void TextureCubemap::create()
m_width, m_height, // Image width/height
0, // Always 0 (legacy)
m_dataFormat, // Texture source format
GL_UNSIGNED_BYTE, // Texture source datatype
m_dataType, // Texture source datatype
data); // Image data
// Clean resources
@@ -224,4 +279,62 @@ void TextureCubemap::create()
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
// -----------------------------------------
std::shared_ptr<TextureFramebuffer> TextureFramebuffer::create(
std::string_view path,
uint32_t width, uint32_t height, uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType)
{
auto result = std::shared_ptr<TextureFramebuffer>(new TextureFramebuffer(path));
result->init(width, height, internalFormat, dataFormat, dataType);
result->createImpl();
return result;
}
void TextureFramebuffer::bind(uint32_t unit) const
{
// Set active unit
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, m_id);
// Reset unit
glActiveTexture(GL_TEXTURE0);
}
void TextureFramebuffer::unbind() const
{
glBindTexture(GL_TEXTURE_2D, 0);
}
void TextureFramebuffer::createImpl()
{
m_id = UINT_MAX;
// Create texture object
glGenTextures(1, &m_id);
// Bind texture object
glBindTexture(GL_TEXTURE_2D, m_id);
// Generate texture
glTexImage2D(
GL_TEXTURE_2D, // Texture target
0, // Midmap level, base starts at level 0
m_internalFormat, // Texture format
m_width, m_height, // Image width/height
0, // Always 0 (legacy)
m_dataFormat, // Texture source format
m_dataType, // Texture source datatype
NULL); // Image data
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Unbind texture object
glBindTexture(GL_TEXTURE_2D, 0);
}
} // namespace Inferno
+41 -12
View File
@@ -10,20 +10,23 @@
#include <memory> // std::shared_ptr
#include <string_view>
#include "glad/glad.h"
#include "inferno/asset/asset-manager.h"
struct aiTexture;
namespace Inferno {
class Texture2D;
class TextureCubemap;
class Texture : public Asset {
public:
virtual ~Texture();
static void savePNG(std::string_view path, std::shared_ptr<Texture> texture);
static void saveScreenshotPNG(std::string_view path, uint32_t width, uint32_t height);
void init(uint32_t width, uint32_t height, uint8_t channels);
void init(uint32_t width, uint32_t height, uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType);
virtual void bind(uint32_t unit = 0) const = 0;
virtual void unbind() const = 0;
@@ -33,12 +36,11 @@ public:
uint32_t id() const { return m_id; }
uint32_t internalFormat() const { return m_internalFormat; }
uint32_t dataFormat() const { return m_dataFormat; }
uint32_t dataType() const { return m_dataType; }
virtual bool is2D() const { return false; }
virtual bool isCubemap() const { return false; }
friend Texture2D;
friend TextureCubemap;
virtual bool isTexture2D() const override { return false; }
virtual bool isTextureCubemap() const override { return false; }
virtual bool isTextureFramebuffer() const override { return false; }
protected:
Texture(std::string_view path)
@@ -52,6 +54,7 @@ protected:
uint32_t m_id { 0 };
uint32_t m_internalFormat { 0 };
uint32_t m_dataFormat { 0 };
uint32_t m_dataType { GL_UNSIGNED_BYTE };
private:
virtual bool isTexture() const override { return true; }
@@ -76,10 +79,9 @@ private:
{
}
virtual bool isTexture2D() const override { return true; }
void createImpl(unsigned char* data);
private:
void create(unsigned char* data);
virtual bool isTexture2D() const override { return true; }
};
// -------------------------------------
@@ -100,10 +102,34 @@ private:
{
}
void createImpl();
virtual bool isTextureCubemap() const override { return true; }
};
// -----------------------------------------
class TextureFramebuffer final : public Texture {
public:
virtual ~TextureFramebuffer() = default;
// Factory function
static std::shared_ptr<TextureFramebuffer> create(
std::string_view path,
uint32_t width, uint32_t height, uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType = GL_UNSIGNED_BYTE);
virtual void bind(uint32_t unit) const override;
virtual void unbind() const override;
private:
void create();
TextureFramebuffer(std::string_view path)
: Texture(path)
{
}
void createImpl();
virtual bool isTextureFramebuffer() const override { return true; }
};
// -----------------------------------------
@@ -117,6 +143,9 @@ inline bool Asset::fastIs<Texture2D>() const { return isTexture2D(); }
template<>
inline bool Asset::fastIs<TextureCubemap>() const { return isTextureCubemap(); }
template<>
inline bool Asset::fastIs<TextureFramebuffer>() const { return isTextureFramebuffer(); }
// clang-format on
} // namespace Inferno
+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);
+5 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 Riyyi
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
@@ -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());
}
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 Riyyi
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
@@ -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
+167 -116
View File
@@ -4,12 +4,17 @@
* SPDX-License-Identifier: MIT
*/
#include "glad/glad.h"
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint8_t, uint32_t
#include <memory> // std::shared_ptr
#include <string>
#include <utility> // std::pair
#include "inferno/core.h"
#include "inferno/render/buffer.h"
#include "glad/glad.h"
#include "ruc/meta/assert.h"
#include "inferno/render/buffer.h"
namespace Inferno {
// -----------------------------------------
@@ -37,113 +42,78 @@ uint32_t BufferElement::getTypeGL() const
return BufferElement::getTypeGL(m_type);
}
uint32_t BufferElement::getTypeSize(const BufferElementType type)
uint32_t BufferElement::getTypeSize(BufferElementType type)
{
switch (type) {
case BufferElementType::None:
return 0;
case BufferElementType::Bool:
return sizeof(bool);
case BufferElementType::Bool2:
return sizeof(bool) * 2;
case BufferElementType::Bool3:
return sizeof(bool) * 3;
case BufferElementType::Bool4:
return sizeof(bool) * 4;
return sizeof(bool) * getTypeCount(type);
case BufferElementType::Int:
return sizeof(int32_t);
case BufferElementType::Int2:
return sizeof(int32_t) * 2;
case BufferElementType::Int3:
return sizeof(int32_t) * 3;
case BufferElementType::Int4:
return sizeof(int32_t) * 4;
return sizeof(int32_t) * getTypeCount(type);
case BufferElementType::Uint:
return sizeof(uint32_t);
case BufferElementType::Uint2:
return sizeof(uint32_t) * 2;
case BufferElementType::Uint3:
return sizeof(uint32_t) * 3;
case BufferElementType::Uint4:
return sizeof(uint32_t) * 4;
return sizeof(uint32_t) * getTypeCount(type);
case BufferElementType::Float:
return sizeof(float);
case BufferElementType::Vec2:
return sizeof(float) * 2;
case BufferElementType::Vec3:
return sizeof(float) * 3;
case BufferElementType::Vec4:
return sizeof(float) * 4;
return sizeof(float) * getTypeCount(type);
case BufferElementType::Double:
return sizeof(double);
case BufferElementType::Double2:
return sizeof(double);
case BufferElementType::Double3:
return sizeof(double);
case BufferElementType::Double4:
return sizeof(double);
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
return sizeof(double) * getTypeCount(type);
case BufferElementType::Mat2:
return sizeof(float) * 2 * 2;
case BufferElementType::Mat3:
return sizeof(float) * 3 * 3;
case BufferElementType::Mat4:
return sizeof(float) * 4 * 4;
case BufferElementType::DoubleMat2:
return sizeof(double) * 2 * 2;
case BufferElementType::DoubleMat3:
return sizeof(double) * 3 * 3;
case BufferElementType::DoubleMat4:
return sizeof(double) * 4 * 4;
return sizeof(float) * getTypeCount(type);
case BufferElementType::MatDouble2:
case BufferElementType::MatDouble3:
case BufferElementType::MatDouble4:
return sizeof(double) * getTypeCount(type);
};
VERIFY(false, "BufferElement unknown BufferElementType size!");
return 0;
}
uint32_t BufferElement::getTypeCount(const BufferElementType type)
uint32_t BufferElement::getTypeCount(BufferElementType type)
{
switch (type) {
case BufferElementType::None:
return 0;
case BufferElementType::Bool:
return 1;
case BufferElementType::Bool2:
return 2;
case BufferElementType::Bool3:
return 3;
case BufferElementType::Bool4:
return 4;
case BufferElementType::Int:
return 1;
case BufferElementType::Int2:
return 2;
case BufferElementType::Int3:
return 3;
case BufferElementType::Int4:
return 4;
case BufferElementType::Uint:
return 1;
case BufferElementType::Uint2:
return 2;
case BufferElementType::Uint3:
return 3;
case BufferElementType::Uint4:
return 4;
case BufferElementType::Float:
return 1;
case BufferElementType::Vec2:
return 2;
case BufferElementType::Vec3:
return 3;
case BufferElementType::Vec4:
return 4;
case BufferElementType::Double:
return 1;
case BufferElementType::Double2:
case BufferElementType::Bool2:
case BufferElementType::Int2:
case BufferElementType::Uint2:
case BufferElementType::Vec2:
case BufferElementType::Vec2Double:
return 2;
case BufferElementType::Double3:
case BufferElementType::Bool3:
case BufferElementType::Int3:
case BufferElementType::Uint3:
case BufferElementType::Vec3:
case BufferElementType::Vec3Double:
return 3;
case BufferElementType::Double4:
case BufferElementType::Bool4:
case BufferElementType::Int4:
case BufferElementType::Uint4:
case BufferElementType::Vec4:
case BufferElementType::Vec4Double:
return 4;
case BufferElementType::Mat2:
return 2 * 2;
@@ -151,11 +121,11 @@ uint32_t BufferElement::getTypeCount(const BufferElementType type)
return 3 * 3;
case BufferElementType::Mat4:
return 4 * 4;
case BufferElementType::DoubleMat2:
case BufferElementType::MatDouble2:
return 2 * 2;
case BufferElementType::DoubleMat3:
case BufferElementType::MatDouble3:
return 3 * 3;
case BufferElementType::DoubleMat4:
case BufferElementType::MatDouble4:
return 4 * 4;
};
@@ -163,62 +133,43 @@ uint32_t BufferElement::getTypeCount(const BufferElementType type)
return 0;
}
uint32_t BufferElement::getTypeGL(const BufferElementType type)
uint32_t BufferElement::getTypeGL(BufferElementType type)
{
switch (type) {
case BufferElementType::None:
return GL_NONE;
case BufferElementType::Bool:
return GL_BOOL;
case BufferElementType::Bool2:
return GL_BOOL;
case BufferElementType::Bool3:
return GL_BOOL;
case BufferElementType::Bool4:
return GL_BOOL;
case BufferElementType::Int:
return GL_INT;
case BufferElementType::Int2:
return GL_INT;
case BufferElementType::Int3:
return GL_INT;
case BufferElementType::Int4:
return GL_INT;
case BufferElementType::Uint:
return GL_UNSIGNED_INT;
case BufferElementType::Uint2:
return GL_UNSIGNED_INT;
case BufferElementType::Uint3:
return GL_UNSIGNED_INT;
case BufferElementType::Uint4:
return GL_UNSIGNED_INT;
case BufferElementType::Float:
return GL_FLOAT;
case BufferElementType::Vec2:
return GL_FLOAT;
case BufferElementType::Vec3:
return GL_FLOAT;
case BufferElementType::Vec4:
return GL_FLOAT;
case BufferElementType::Double:
return GL_DOUBLE;
case BufferElementType::Double2:
return GL_DOUBLE;
case BufferElementType::Double3:
return GL_DOUBLE;
case BufferElementType::Double4:
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
return GL_DOUBLE;
case BufferElementType::Mat2:
return GL_FLOAT;
case BufferElementType::Mat3:
return GL_FLOAT;
case BufferElementType::Mat4:
return GL_FLOAT;
case BufferElementType::DoubleMat2:
return GL_DOUBLE;
case BufferElementType::DoubleMat3:
return GL_DOUBLE;
case BufferElementType::DoubleMat4:
case BufferElementType::MatDouble2:
case BufferElementType::MatDouble3:
case BufferElementType::MatDouble4:
return GL_DOUBLE;
};
@@ -226,6 +177,58 @@ uint32_t BufferElement::getTypeGL(const BufferElementType type)
return 0;
}
uint32_t BufferElement::getGLTypeSize(uint32_t type)
{
switch (type) {
case GL_BOOL:
case GL_INT:
case GL_UNSIGNED_INT:
case GL_FLOAT:
return 4;
case GL_BOOL_VEC2:
case GL_INT_VEC2:
case GL_UNSIGNED_INT_VEC2:
case GL_FLOAT_VEC2:
return 4 * 2;
case GL_BOOL_VEC3:
case GL_INT_VEC3:
case GL_UNSIGNED_INT_VEC3:
case GL_FLOAT_VEC3:
return 4 * 3;
case GL_BOOL_VEC4:
case GL_INT_VEC4:
case GL_UNSIGNED_INT_VEC4:
case GL_FLOAT_VEC4:
return 4 * 4;
case GL_FLOAT_MAT2:
return 4 * 2 * 2;
case GL_FLOAT_MAT3:
return 4 * 3 * 3;
case GL_FLOAT_MAT4:
return 4 * 4 * 4;
case GL_DOUBLE:
return 8;
case GL_DOUBLE_VEC2:
return 8 * 2;
case GL_DOUBLE_VEC3:
return 8 * 3;
case GL_DOUBLE_VEC4:
return 8 * 4;
case GL_DOUBLE_MAT2:
return 8 * 2 * 2;
case GL_DOUBLE_MAT3:
return 8 * 3 * 3;
case GL_DOUBLE_MAT4:
return 8 * 4 * 4;
default:
VERIFY_NOT_REACHED();
};
return 0;
}
// -----------------------------------------
BufferLayout::BufferLayout(const std::initializer_list<BufferElement>& elements)
@@ -239,30 +242,25 @@ void BufferLayout::calculateOffsetsAndStride()
m_stride = 0;
for (auto& element : m_elements) {
element.setOffset(m_stride);
m_stride += element.getSize();
m_stride += element.size();
}
}
// -----------------------------------------
VertexBuffer::VertexBuffer(size_t size)
: VertexBuffer(size, nullptr)
{
glGenBuffers(1, &m_id);
bind();
// Reserve data on the GPU
glBufferData(GL_ARRAY_BUFFER, size, nullptr, GL_DYNAMIC_DRAW);
unbind();
}
VertexBuffer::VertexBuffer(float* vertices, size_t size)
VertexBuffer::VertexBuffer(size_t size, float* vertices)
{
m_id = UINT_MAX;
glGenBuffers(1, &m_id);
bind();
// Upload data to the GPU
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_DYNAMIC_DRAW);
unbind();
}
@@ -297,11 +295,12 @@ void VertexBuffer::uploadData(const void* data, uint32_t size)
IndexBuffer::IndexBuffer(uint32_t* indices, size_t size)
: m_count(size / sizeof(uint32_t))
{
m_id = UINT_MAX;
glCreateBuffers(1, &m_id);
bind();
// Upload data to the GPU
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_DYNAMIC_DRAW);
unbind();
}
@@ -335,6 +334,7 @@ void IndexBuffer::uploadData(const void* data, uint32_t size)
VertexArray::VertexArray()
{
m_id = UINT_MAX;
glCreateVertexArrays(1, &m_id);
}
@@ -355,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();
@@ -364,20 +364,71 @@ void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
uint32_t index = 0;
for (const auto& element : layout) {
glEnableVertexAttribArray(index);
switch (element.type()) {
case BufferElementType::None:
break;
case BufferElementType::Int:
case BufferElementType::Int2:
case BufferElementType::Int3:
case BufferElementType::Int4:
case BufferElementType::Uint:
case BufferElementType::Uint2:
case BufferElementType::Uint3:
case BufferElementType::Uint4: {
glVertexAttribIPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
layout.stride(),
reinterpret_cast<const void*>(element.offset()));
break;
}
case BufferElementType::Bool:
case BufferElementType::Bool2:
case BufferElementType::Bool3:
case BufferElementType::Bool4:
case BufferElementType::Float:
case BufferElementType::Vec2:
case BufferElementType::Vec3:
case BufferElementType::Vec4:
case BufferElementType::Mat2:
case BufferElementType::Mat3:
case BufferElementType::Mat4: {
glVertexAttribPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
element.getNormalized() ? GL_TRUE : GL_FALSE,
layout.getStride(),
reinterpret_cast<const void*>(element.getOffset()));
element.normalized() ? GL_TRUE : GL_FALSE,
layout.stride(),
reinterpret_cast<const void*>(element.offset()));
break;
}
case BufferElementType::Double:
case BufferElementType::Vec2Double:
case BufferElementType::Vec3Double:
case BufferElementType::Vec4Double:
case BufferElementType::MatDouble2:
case BufferElementType::MatDouble3:
case BufferElementType::MatDouble4: {
glVertexAttribLPointer(
index,
element.getTypeCount(),
element.getTypeGL(),
layout.stride(),
reinterpret_cast<const void*>(element.offset()));
break;
}
default:
VERIFY_NOT_REACHED();
};
index++;
}
m_vertexBuffers.push_back(std::move(vertexBuffer));
unbind();
vertexBuffer->unbind();
m_vertexBuffers.push_back(std::move(vertexBuffer));
}
void VertexArray::setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer)
@@ -385,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
+34 -31
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
@@ -7,52 +7,54 @@
#pragma once
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint32_t
#include <cstdint> // int32_t, uint8_t, uint32_t
#include <memory> // std::shared_ptr
#include <string> // std::string
#include <vector> // std::vector
#include <string>
#include <vector>
namespace Inferno {
// clang-format off
// https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)
enum class BufferElementType {
enum class BufferElementType : uint8_t {
None = 0,
Bool, Bool2, Bool3, Bool4, // bvec
Int, Int2, Int3, Int4, // ivec
Uint, Uint2, Uint3, Uint4, // uvec
Float, Vec2, Vec3, Vec4, // vec
Double, Double2, Double3, Double4, // dvec
Double, Vec2Double, Vec3Double, Vec4Double, // dvec
Mat2, Mat3, Mat4, // mat
DoubleMat2, DoubleMat3, DoubleMat4, // dmat
MatDouble2, MatDouble3, MatDouble4, // dmat
};
// clang-format on
// -----------------------------------------
// Describes one element of the BufferLayout
class BufferElement {
class BufferElement final {
public:
BufferElement(BufferElementType type, std::string name, bool normalized = false);
~BufferElement() = default;
uint32_t getTypeSize() const;
uint32_t getTypeCount() const;
uint32_t getTypeGL() const;
static uint32_t getTypeSize(const BufferElementType type);
static uint32_t getTypeCount(const BufferElementType type);
static uint32_t getTypeGL(const BufferElementType type);
static uint32_t getTypeSize(BufferElementType type);
static uint32_t getTypeCount(BufferElementType type);
static uint32_t getTypeGL(BufferElementType type);
static uint32_t getGLTypeSize(uint32_t type);
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;
@@ -65,13 +67,14 @@ private:
// -----------------------------------------
// Layout that describes raw vertex data
class BufferLayout {
class BufferLayout final {
public:
BufferLayout() {}
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(); }
@@ -90,10 +93,10 @@ private:
// -----------------------------------------
// GPU memory which holds raw vertex data
class VertexBuffer { // Vertex Buffer Object, VBO
class VertexBuffer final { // Vertex Buffer Object, VBO
public:
VertexBuffer(size_t size);
VertexBuffer(float* vertices, size_t size);
VertexBuffer(size_t size, float* vertices);
~VertexBuffer();
void bind() const;
@@ -101,9 +104,9 @@ public:
void uploadData(const void* data, uint32_t size);
const BufferLayout& getLayout() const { return m_layout; }
const BufferLayout& layout() const { return m_layout; }
inline void setLayout(const BufferLayout& layout) { m_layout = layout; }
void setLayout(const BufferLayout& layout) { m_layout = layout; }
private:
uint32_t m_id { 0 };
@@ -113,7 +116,7 @@ private:
// -----------------------------------------
// Vertices order of rendering
class IndexBuffer { // Element Buffer Object, EBO
class IndexBuffer final { // Element Buffer Object, EBO
public:
IndexBuffer(uint32_t* indices, size_t size);
~IndexBuffer();
@@ -123,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 };
@@ -133,7 +136,7 @@ private:
// -----------------------------------------
// Array that holds the vertex attributes configuration
class VertexArray { // Vertex Array Object, VAO
class VertexArray final { // Vertex Array Object, VAO
public:
VertexArray();
~VertexArray();
@@ -145,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 };
+140 -2
View File
@@ -1,21 +1,159 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <cstdint> // uint8_t, uint32_t
#include "glad/glad.h"
#include "ruc/meta/assert.h"
#include "inferno/asset/texture.h"
#include "inferno/render/framebuffer.h"
namespace Inferno {
Framebuffer::Framebuffer()
std::shared_ptr<Framebuffer> Framebuffer::create(const Properties& properties)
{
VERIFY((properties.attachments.size() > 0 && !properties.renderToScreen) || properties.renderToScreen,
"cant have attachments on the default framebuffer");
auto result = std::shared_ptr<Framebuffer>(new Framebuffer(properties));
result->createTextures();
return result;
}
Framebuffer::~Framebuffer()
{
if (m_renderToScreen) {
return;
}
glDeleteFramebuffers(1, &m_id);
}
void Framebuffer::copyBuffer(std::shared_ptr<Framebuffer> from, std::shared_ptr<Framebuffer> to, uint32_t bits, uint32_t filter)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, from->m_id);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // write to default framebuffer
glBlitFramebuffer(0, 0, from->m_width, from->m_height, 0, 0, to->m_width, to->m_height, bits, filter);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// -----------------------------------------
void Framebuffer::bind() const
{
glBindFramebuffer(GL_FRAMEBUFFER, m_id);
}
void Framebuffer::unbind() const
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
bool Framebuffer::check() const
{
VERIFY(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE,
"malformed framebuffer: {:#x}", glCheckFramebufferStatus(GL_FRAMEBUFFER));
return true;
}
void Framebuffer::resize(uint32_t width, uint32_t height)
{
if (m_width == width && m_height == height) {
return;
}
m_width = width;
m_height = height;
createTextures();
}
// -----------------------------------------
void Framebuffer::createTextures()
{
if (m_renderToScreen) {
return;
}
if (m_id) {
glDeleteFramebuffers(1, &m_id);
m_textures.clear();
}
m_id = UINT_MAX;
glGenFramebuffers(1, &m_id);
bind();
auto it = m_attachments.begin();
m_colorAttachmentCount = 0;
size_t size = m_attachments.size();
m_textures.resize(size);
for (size_t i = 0; i < size; ++i) {
TypeProperties type = *(it + i);
if (type.type == Type::RGB8) {
// Set color attachment 0 out of 32
m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGB8, GL_RGB);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0);
m_colorAttachmentCount++;
continue;
}
if (type.type == Type::RGBA8) { // Color
// Set color attachment 0 out of 32
m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGBA8, GL_RGBA);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0);
m_colorAttachmentCount++;
continue;
}
if (type.type == Type::RGBA16F) {
// Set color attachment 0 out of 32
m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGBA16F, GL_RGBA, GL_FLOAT);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0);
m_colorAttachmentCount++;
continue;
}
if (type.type == Type::RGBA32F) {
// Set color attachment 0 out of 32
m_textures[i] = TextureFramebuffer::create("", m_width, m_height, GL_RGBA32F, GL_RGBA, GL_FLOAT);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + m_colorAttachmentCount, GL_TEXTURE_2D, m_textures[i]->id(), 0);
m_colorAttachmentCount++;
continue;
}
// This combined texture is required for older GPUs
if (type.type == Type::Depth24Stencil8) { // Depth
m_textures[i] = (TextureFramebuffer::create(
"", m_width, m_height, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8));
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_textures[i]->id(), 0);
continue;
}
if (type.type == Type::Depth32F) {
// FIXME: This isnt a 32-bit float texture
m_textures[i] = (TextureFramebuffer::create(
"", m_width, m_height, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT));
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_textures[i]->id(), 0);
continue;
}
}
VERIFY(m_colorAttachmentCount <= 32, "maximum color attachments was exceeded: {}/32", m_colorAttachmentCount);
check();
unbind();
}
} // namespace Inferno
+92 -4
View File
@@ -1,17 +1,105 @@
/*
* Copyright (C) 2022 Riyyi
* Copyright (C) 2022,2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstddef> // size_t
#include <cstdint> // uint8_t
#include <initializer_list>
#include <memory> // std::shared_ptr
#include <vector>
#include "glm/ext/vector_float4.hpp" // glm::vec4
#include "inferno/asset/texture.h"
namespace Inferno {
class Framebuffer {
class Framebuffer final { // Frame Buffer Object, FBO
public:
Framebuffer();
virtual ~Framebuffer();
enum Type : uint8_t {
None = 0,
// Color
RGB8 = 1,
RGBA8 = 2,
RGBA16F = 3,
RGBA32F = 4,
// Depth/stencil
Depth24Stencil8 = 5,
Depth32F = 6,
// Defaults
Color = RGBA8,
Depth = Depth24Stencil8,
};
struct TypeProperties {
TypeProperties() = default;
TypeProperties(Type type)
: type(type)
{
}
Type type;
};
struct Properties {
std::initializer_list<TypeProperties> attachments {};
bool renderToScreen { false }; // (dummy framebuffer)
uint32_t width { 1280 };
uint32_t height { 720 };
glm::vec4 clearColor { 1.0f, 0.0f, 1.0f, 1.0f }; // magenta
uint32_t clearBit { 0 };
};
~Framebuffer();
// Factory function
static std::shared_ptr<Framebuffer> create(const Properties& properties);
static void copyBuffer(std::shared_ptr<Framebuffer> from, std::shared_ptr<Framebuffer> to, uint32_t bits, uint32_t filter);
void bind() const;
void unbind() const;
bool check() const;
void resize(uint32_t width, uint32_t height);
uint8_t colorAttachmentCount() const { return m_colorAttachmentCount; }
uint32_t id() const { return m_id; }
uint32_t width() const { return m_width; }
uint32_t height() const { return m_height; }
uint32_t clearBit() const { return m_clearBit; }
glm::vec4 clearColor() const { return m_clearColor; }
const std::vector<TypeProperties>& attachments() const { return m_attachments; }
std::shared_ptr<TextureFramebuffer> texture(size_t index) const { return m_textures[index]; }
private:
Framebuffer(const Properties& properties)
: m_renderToScreen(properties.renderToScreen)
, m_width(properties.width)
, m_height(properties.height)
, m_clearBit(properties.clearBit)
, m_clearColor(properties.clearColor)
, m_attachments(properties.attachments)
{
}
void createTextures();
private:
bool m_renderToScreen { false };
uint8_t m_colorAttachmentCount { 1 };
uint32_t m_id { 0 };
uint32_t m_width { 0 };
uint32_t m_height { 0 };
uint32_t m_clearBit { 0 };
glm::vec4 m_clearColor;
std::vector<TypeProperties> m_attachments;
std::vector<std::shared_ptr<TextureFramebuffer>> m_textures;
};
} // namespace Inferno
-347
View File
@@ -1,347 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#if 0
#include <algorithm> // std::copy
#include <utility> // std::move
#include "nlohmann/json.hpp"
#include "ruc/meta/assert.h"
#include "inferno/io/file.h"
#include "inferno/io/gltffile.h"
#include "inferno/io/log.h"
#include "inferno/render/gltf.h"
#include "inferno/util/integer.h"
namespace Inferno {
Gltf::Gltf(const std::string& path)
: m_path(std::move(path))
{
auto gltf = GltfFile::read(path);
VERIFY(gltf.first, "Gltf model invalid JSON content '{}'", path);
json json = json::parse(gltf.first.get());
// Add binary data from .glb files
if (gltf.second) {
m_model.data.emplace(0, std::move(gltf.second));
}
// Properties
// Asset
// ---------------------------------
VERIFY(Json::hasProperty(json, "asset"), "GlTF model missing required property 'asset'");
auto asset = json["asset"];
VERIFY(asset.is_object(), "Gltf model invalid property type 'asset'");
parseAsset(&m_model.asset, asset);
// Scene
// ---------------------------------
if (Json::hasProperty(json, "scenes")) {
auto scenes = json["scenes"];
VERIFY(scenes.is_array(), "Gltf model invalid property type 'scenes'");
for (auto& [key, object] : scenes.items()) {
glTF::Scene scene;
parseScene(&scene, key, object);
m_model.scenes.emplace_back(std::move(scene));
}
}
// Node
// ---------------------------------
if (Json::hasProperty(json, "nodes")) {
auto nodes = json["nodes"];
VERIFY(nodes.is_array(), "Gltf model invalid property type 'nodes'");
for (auto& [key, object] : nodes.items()) {
glTF::Node node;
parseNode(&node, key, object);
m_model.nodes.emplace_back(std::move(node));
}
}
// Mesh
// ---------------------------------
if (Json::hasProperty(json, "meshes")) {
auto meshes = json["meshes"];
VERIFY(meshes.is_array(), "Gltf model invalid property type 'meshes'");
for (auto& [key, object] : meshes.items()) {
glTF::Mesh mesh;
parseMesh(&mesh, key, object);
m_model.meshes.emplace_back(std::move(mesh));
}
}
// Accessor
// ---------------------------------
if (Json::hasProperty(json, "accessors")) {
auto accessors = json["accessors"];
VERIFY(accessors.is_array(), "Gltf model invalid property type 'accessors'");
for (auto& [key, object] : accessors.items()) {
glTF::Accessor accessor;
parseAccessor(&accessor, key, object);
m_model.accessors.emplace_back(std::move(accessor));
}
}
// Bufferview
// ---------------------------------
if (Json::hasProperty(json, "bufferViews")) {
auto bufferViews = json["bufferViews"];
VERIFY(bufferViews.is_array(), "Gltf model invalid property type 'bufferViews'");
for (auto& [key, object] : bufferViews.items()) {
glTF::BufferView bufferView;
parseBufferView(&bufferView, key, object);
m_model.bufferViews.emplace_back(std::move(bufferView));
}
}
// Buffer
// ---------------------------------
if (Json::hasProperty(json, "buffers")) {
auto buffers = json["buffers"];
VERIFY(buffers.is_array(), "Gltf model invalid property type 'buffers'");
for (auto& [key, object] : buffers.items()) {
glTF::Buffer buffer;
parseBuffer(&buffer, key, object, &m_model.data);
m_model.buffers.emplace_back(std::move(buffer));
}
}
}
Gltf::~Gltf()
{
}
void Gltf::parseAsset(glTF::Asset* asset, const json& object)
{
auto copyright = Json::parseStringProperty(object, "copyright", false);
auto generator = Json::parseStringProperty(object, "generator", false);
auto version = Json::parseStringProperty(object, "version", true);
VERIFY(version, "GlTF model missing required property 'version'");
VERIFY(version.value().compare("2.0") == 0, "GlTF version unsupported '{}'", version.value());
auto minVersion = Json::parseStringProperty(object, "minVersion", false);
if (copyright) asset->copyright = copyright.value();
if (generator) asset->generator = generator.value();
if (version) asset->version = version.value();
if (minVersion) asset->minVersion = minVersion.value();
}
void Gltf::parseScene(glTF::Scene* scene, const std::string& key, const json& object)
{
auto nodes = Json::parseFloatArrayProperty(object, "nodes", false);
VERIFY(!nodes || nodes.value().size() > 0, "Gltf scene '{}' empty 'nodes' property", key);
auto name = Json::parseStringProperty(object, "name", false);
scene->name = name ? name.value() : key;
}
void Gltf::parseNode(glTF::Node* node, const std::string& key, const json& object)
{
auto camera = Json::parseUnsignedProperty(object, "camera", false);
auto children = Json::parseUnsignedArrayProperty(object, "children", false);
VERIFY(!children || children.value().size() > 0, "Gltf node '{}' empty property 'children'", key);
auto skin = Json::parseUnsignedProperty(object, "skin", false);
auto matrix = Json::parseFloatArrayProperty(object, "matrix", false);
VERIFY(!matrix || matrix.value().size() == 16, "Gltf node '{}' property 'matrix' invalid size", key);
auto mesh = Json::parseUnsignedProperty(object, "mesh", false);
auto rotation = Json::parseFloatArrayProperty(object, "rotation", false);
VERIFY(!rotation || rotation.value().size() == 4, "Gltf node '{}' property 'rotation' invalid size", key);
auto scale = Json::parseFloatArrayProperty(object, "scale", false);
VERIFY(!scale || scale.value().size() == 3, "Gltf node '{}' property 'scale' invalid size", key);
auto translation = Json::parseFloatArrayProperty(object, "translation", false);
VERIFY(!translation || translation.value().size() == 3, "Gltf node '{}' property 'translation' invalid size", key);
auto weights = Json::parseFloatArrayProperty(object, "weights", false);
VERIFY(!weights || weights.value().size() > 0, "Gltf node '{}' empty property 'weights'", key);
auto name = Json::parseStringProperty(object, "name", false);
if (camera) node->camera = camera.value();
if (children) node->children = children.value();
if (skin) node->skin = skin.value();
if (matrix) std::copy(matrix.value().begin(), matrix.value().end(), node->matrix.begin());
if (mesh) node->mesh = mesh.value();
if (rotation) std::copy(rotation.value().begin(), rotation.value().end(), node->rotation.begin());
if (scale) std::copy(scale.value().begin(), scale.value().end(), node->scale.begin());
if (translation) std::copy(translation.value().begin(), translation.value().end(), node->translation.begin());
if (weights) node->weights = weights.value();
node->name = name ? name.value() : key;
}
void Gltf::parsePrimitive(glTF::Primitive* primitive, const std::string& key, const json& object)
{
auto attributes = Json::parseUnsignedObjectProperty(object, "attributes", true);
VERIFY(attributes && attributes.value().size() > 0, "Gltf primitive '{}' empty property 'attributes'", key);
auto indices = Json::parseUnsignedProperty(object, "indices", false);
auto material = Json::parseUnsignedProperty(object, "material", false);
auto mode = Json::parseUnsignedProperty(object, "mode", false);
if (Json::hasProperty(object, "targets")) {
auto targets = object["targets"];
VERIFY(targets.is_array(), "Gltf primitive '{}' property 'targets' invalid type", key);
for (auto& targetObject : targets) {
std::map<std::string, uint32_t> target;
for (auto& [key, propertyValue] : targetObject.items()) {
auto value = Json::getPropertyValue<uint32_t>(propertyValue, json::value_t::number_unsigned);
if (value) target.emplace(std::move(key), value.value());
}
VERIFY(target.size() > 0, "Gltf primitive '{}' empty 'target' object", key);
primitive->targets.emplace_back(std::move(target));
}
}
if (attributes) primitive->attributes = attributes.value();
if (indices) primitive->indices = indices.value();
if (material) primitive->material = material.value();
if (mode) primitive->mode = static_cast<unsigned char>(mode.value());
}
void Gltf::parseMesh(glTF::Mesh* mesh, const std::string& key, const json& object)
{
VERIFY(Json::hasProperty(object, "primitives"), "Gltf mesh '{}' missing required property 'primitives'", key);
auto primitives = object["primitives"];
VERIFY(primitives.is_array(), "Gltf mesh '{}' property 'primitives' invalid type", key);
for (auto& primitiveObject : primitives) {
glTF::Primitive primitive;
parsePrimitive(&primitive, key, primitiveObject);
mesh->primitives.emplace_back(std::move(primitive));
}
auto weights = Json::parseFloatArrayProperty(object, "weights", false);
VERIFY(!weights || weights.value().size() > 0, "Gltf mesh '{}' empty property 'weights'", key);
auto name = Json::parseStringProperty(object, "name", false);
if (weights) mesh->weights = weights.value();
mesh->name = name ? name.value() : key;
}
void Gltf::parseAccessor(glTF::Accessor* accessor, const std::string& key, const json& object)
{
auto bufferView = Json::parseUnsignedProperty(object, "bufferView", false);
auto byteOffset = Json::parseUnsignedProperty(object, "byteOffset", false);
auto componentType = Json::parseUnsignedProperty(object, "componentType", true);
VERIFY(componentType, "Gltf accessor '{}' missing required property 'componentType'", key);
auto normalized = Json::parseBoolProperty(object, "normalized", false);
auto count = Json::parseUnsignedProperty(object, "count", true);
VERIFY(count, "Gltf accessor '{}' missing required property 'count'", key);
auto type = Json::parseStringProperty(object, "type", true);
VERIFY(type, "Gltf accessor '{}' missing required property 'type'", key);
auto max = Json::parseFloatArrayProperty(object, "max", false);
VERIFY(!max || max.value().size() > 0, "Gltf accessor '{}' empty property 'max'", key);
auto min = Json::parseFloatArrayProperty(object, "min", false);
VERIFY(!min || min.value().size() > 0, "Gltf accessor '{}' empty property 'min'", key);
auto name = Json::parseStringProperty(object, "name", false);
if (bufferView) accessor->bufferView = bufferView.value();
if (byteOffset) accessor->byteOffset = byteOffset.value();
if (normalized) accessor->normalized = normalized.value();
if (count) accessor->count = count.value();
if (type) accessor->type = type.value();
if (max) accessor->max = max.value();
if (min) accessor->min = min.value();
accessor->name = name ? name.value() : key;
}
void Gltf::parseBufferView(glTF::BufferView* bufferView, const std::string& key, const json& object)
{
auto buffer = Json::parseUnsignedProperty(object, "buffer", false);
VERIFY(buffer, "Gltf bufferView '{}' missing required property 'buffer'", key);
auto byteOffset = Json::parseUnsignedProperty(object, "byteOffset", false);
auto byteLength = Json::parseUnsignedProperty(object, "byteLength", true);
VERIFY(byteLength, "Gltf bufferView '{}' missing required property 'byteLength'", key);
auto byteStride = Json::parseUnsignedProperty(object, "byteStride", false);
auto target = Json::parseUnsignedProperty(object, "target", false);
auto name = Json::parseStringProperty(object, "name", false);
if (buffer) bufferView->buffer = buffer.value();
if (byteOffset) bufferView->byteOffset = byteOffset.value();
if (byteLength) bufferView->byteLength = byteLength.value();
if (byteStride) bufferView->byteStride = byteStride.value();
if (target) bufferView->target = target.value();
bufferView->name = name ? name.value() : key;
}
void Gltf::parseBuffer(glTF::Buffer* buffer, const std::string& key, const json& object, std::map<uint32_t, std::shared_ptr<char[]>>* data)
{
auto uri = Json::parseStringProperty(object, "uri", false);
// Load external binary data
if (uri) {
VERIFY(uri.value().find("data:", 0) == std::string::npos, "GltfFile embedded binary data is unsupported");
data->emplace(std::stou(key), File::raw("assets/gltf/" + uri.value()));
}
auto byteLength = Json::parseUnsignedProperty(object, "byteLength", true);
VERIFY(byteLength, "Gltf buffer '{}' missing required property 'byteLength'", key);
auto name = Json::parseStringProperty(object, "name", false);
if (uri) buffer->uri = uri.value();
if (byteLength) buffer->byteLength = byteLength.value();
buffer->name = name ? name.value() : key;
}
} // namespace Inferno
#endif
-176
View File
@@ -1,176 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#if 0
#include <cstdint> // uint32_t
#include <memory> // std::shared_ptr
#include <string> // std::string
#include <unordered_map> // std::unordered_map
#include <vector> // std::vector
#include "ruc/singleton.h"
#include "inferno/util/json.h"
#define GLTF_TYPE_SCALAR 1
#define GLTF_TYPE_VEC2 2
#define GLTF_TYPE_VEC3 3
#define GLTF_TYPE_VEC4 4
#define GLTF_TYPE_MAT2 8
#define GLTF_TYPE_MAT3 12
#define GLTF_TYPE_MAT4 16
#define GLTF_COMPONENT_TYPE_BYTE 5120
#define GLTF_COMPONENT_TYPE_UNSIGNED_BYTE 5121
#define GLTF_COMPONENT_TYPE_SHORT 5122
#define GLTF_COMPONENT_TYPE_UNSIGNED_SHORT 5123
#define GLTF_COMPONENT_TYPE_INT 5124
#define GLTF_COMPONENT_TYPE_UNSIGNED_INT 5125
#define GLTF_COMPONENT_TYPE_FLOAT 5126
#define GLTF_TARGET_ARRAY_BUFFER 34962
#define GLTF_TARGET_ELEMENT_ARRAY_BUFFER 34963
namespace Inferno {
namespace glTF {
// Type specifications
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#objects
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#asset
struct Asset {
std::string copyright;
std::string generator;
std::string version; // Required
std::string minVersion;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes
struct Scene {
std::vector<uint32_t> nodes;
std::string name;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy
struct Node {
uint32_t camera;
std::vector<uint32_t> children;
uint32_t skin;
std::array<float, 16> matrix { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; // Identity matrix
uint32_t mesh;
std::array<float, 4> rotation { 0, 0, 0, 1 };
std::array<float, 3> scale { 1, 1, 1 };
std::array<float, 3> translation { 0, 0, 0 };
std::vector<float> weights;
std::string name;
};
struct Primitive {
std::map<std::string, uint32_t> attributes; // Required
uint32_t indices;
uint32_t material;
unsigned char mode { 4 };
std::vector<std::map<std::string, uint32_t>> targets;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#meshes
struct Mesh {
std::vector<Primitive> primitives; // Required
std::vector<float> weights;
std::string name;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#accessors
struct Accessor {
uint32_t bufferView;
uint32_t byteOffset { 0 };
uint32_t componentType; // Required
bool normalized { false };
uint32_t count; // Required
std::string type; // Required
std::vector<float> max;
std::vector<float> min;
std::string name;
};
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#buffers-and-buffer-views
struct BufferView {
uint32_t buffer; // Required
uint32_t byteOffset { 0 };
uint32_t byteLength; // Required
uint32_t byteStride;
uint32_t target;
std::string name;
};
struct Buffer {
std::string uri;
uint32_t byteLength; // Required
std::string name;
};
struct Model {
Asset asset;
std::vector<Scene> scenes;
std::vector<Node> nodes;
std::vector<Mesh> meshes;
std::vector<Accessor> accessors;
std::vector<BufferView> bufferViews;
std::vector<Buffer> buffers;
std::map<uint32_t, std::shared_ptr<char[]>> data;
};
} // namespace glTF
// -----------------------------------------
class Gltf {
public:
Gltf(const std::string& path);
virtual ~Gltf();
inline const glTF::Model& model() const { return m_model; }
private:
static void parseAsset(glTF::Asset* asset, const json& object);
static void parseScene(glTF::Scene* scene, const std::string& key, const json& object);
static void parseNode(glTF::Node* node, const std::string& key, const json& object);
static void parsePrimitive(glTF::Primitive* primitive, const std::string& key, const json& object);
static void parseMesh(glTF::Mesh* mesh, const std::string& key, const json& object);
static void parseAccessor(glTF::Accessor* accessor, const std::string& key, const json& object);
static void parseBufferView(glTF::BufferView* bufferView, const std::string& key, const json& object);
static void parseBuffer(glTF::Buffer* buffer, const std::string& key, const json& object, std::map<uint32_t, std::shared_ptr<char[]>>* data);
std::string m_path;
glTF::Model m_model;
};
// -----------------------------------------
class GltfManager final : ruc::Singleton<GltfManager> {
public:
GltfManager(s) {}
virtual ~GltfManager() {}
void add(const std::string& path, std::shared_ptr<Gltf> gltf);
std::shared_ptr<Gltf> load(const std::string& path);
std::shared_ptr<Gltf> get(const std::string& path);
bool exists(const std::string& path);
void remove(const std::string& path);
void remove(std::shared_ptr<Gltf> gltf);
private:
std::unordered_map<std::string, std::shared_ptr<Gltf>> m_gltfList;
};
} // namespace Inferno
#endif
+27 -4
View File
@@ -4,10 +4,12 @@
* SPDX-License-Identifier: MIT
*/
#include <memory> // std::shadred_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"
@@ -29,9 +31,10 @@ void RenderCommand::destroy()
{
}
void RenderCommand::clear()
void RenderCommand::clearBit(uint32_t bits)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT
glClear(bits);
}
void RenderCommand::clearColor(const glm::vec4& color)
@@ -41,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);
}
@@ -56,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,6 +6,7 @@
#pragma once
#include <cstdint> // int32_t, uint32_t
#include <memory> // std::shadred_ptr
#include "glm/ext/vector_float4.hpp" // glm::vec4
@@ -19,12 +20,13 @@ public:
static void initialize();
static void destroy();
static void clear();
static void clearBit(uint32_t bits);
static void clearColor(const glm::vec4& color);
static void drawIndexed(std::shared_ptr<VertexArray> vertexArray, uint32_t indexCount = 0);
static void setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height);
static void setDepthTest(bool enabled);
static void setColorAttachmentCount(uint32_t count);
static bool depthTest();
static int32_t textureUnitAmount();
+163 -76
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>
@@ -124,32 +121,32 @@ void Renderer<T>::unbind()
template<typename T>
void Renderer<T>::createElementBuffer()
{
// CPU
// ---------------------------------
// CPU
// Generate indices
uint32_t* indices = new uint32_t[maxElements];
uint32_t* elements = new uint32_t[maxElements];
uint32_t offset = 0;
for (uint32_t i = 0; i < maxElements; i += elementPerQuad) {
indices[i + 0] = offset + 0;
indices[i + 1] = offset + 1;
indices[i + 2] = offset + 2;
indices[i + 3] = offset + 2;
indices[i + 4] = offset + 3;
indices[i + 5] = offset + 0;
elements[i + 0] = offset + 0;
elements[i + 1] = offset + 1;
elements[i + 2] = offset + 2;
elements[i + 3] = offset + 2;
elements[i + 4] = offset + 3;
elements[i + 5] = offset + 0;
offset += vertexPerQuad;
}
// GPU
// ---------------------------------
// GPU
// Create index buffer
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * maxElements);
auto indexBuffer = std::make_shared<IndexBuffer>(elements, sizeof(uint32_t) * maxElements);
m_vertexArray->setIndexBuffer(indexBuffer);
delete[] indices;
delete[] elements;
}
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,24 +194,35 @@ void Renderer<T>::nextBatch()
// -----------------------------------------
Renderer2D::Renderer2D(s)
{
Renderer2D::initialize();
}
Renderer2D::~Renderer2D()
{
}
void Renderer2D::initialize()
{
Renderer::initialize();
// CPU
// ---------------------------------
// 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] = { -0.5f, -0.5f, 0.0f, 1.0f };
m_vertexPositions[1] = { 0.5f, -0.5f, 0.0f, 1.0f };
m_vertexPositions[2] = { 0.5f, 0.5f, 0.0f, 1.0f };
m_vertexPositions[3] = { -0.5f, 0.5f, 0.0f, 1.0f };
m_vertexPositions[0] = { -1.0f, -1.0f, 0.0f, 1.0f };
m_vertexPositions[1] = { 1.0f, -1.0f, 0.0f, 1.0f };
m_vertexPositions[2] = { 1.0f, 1.0f, 0.0f, 1.0f };
m_vertexPositions[3] = { -1.0f, 1.0f, 0.0f, 1.0f };
// GPU
// ---------------------------------
// GPU
m_enableDepthBuffer = false;
// Create vertex buffer
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(QuadVertex) * maxVertices);
@@ -221,20 +230,13 @@ Renderer2D::Renderer2D(s)
{ 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 = (float)textureUnitIndex;
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
@@ -281,21 +283,30 @@ 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();
// CPU
// ---------------------------------
// 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
@@ -335,8 +346,8 @@ RendererCubemap::RendererCubemap(s)
m_vertexPositions[22] = { 0.5f, -0.5f, 0.5f, 1.0f };
m_vertexPositions[23] = { 0.5f, -0.5f, -0.5f, 1.0f };
// GPU
// ---------------------------------
// GPU
m_enableDepthBuffer = false;
@@ -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 = (float)textureUnitIndex;
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
@@ -405,15 +416,15 @@ RendererFont::RendererFont(s)
{
Renderer::initialize();
// CPU
// ---------------------------------
// 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
// ---------------------------------
// GPU
m_enableDepthBuffer = false;
@@ -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 = (float)textureUnitIndex;
m_vertexBufferPtr->quad.textureIndex = textureUnitIndex;
m_vertexBufferPtr->width = symbolQuad[i].width;
m_vertexBufferPtr->edge = symbolQuad[i].edge;
@@ -477,86 +492,93 @@ Renderer3D::Renderer3D(s)
{
Renderer::initialize();
// CPU
// ---------------------------------
// 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
// ---------------------------------
// 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> indices, const TransformComponent& transform, std::shared_ptr<Texture> texture)
Renderer3D::~Renderer3D()
{
VERIFY(vertices.size() <= maxVertices, "model vertices too big for buffer");
VERIFY(indices.size() <= maxElements, "model elements too big for buffer");
}
void Renderer3D::drawModel(std::span<const Vertex> vertices, std::span<const uint32_t> elements, const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture)
{
// ruc::error("drawModel");
VERIFY(vertices.size() <= maxVertices, "model vertices too big for buffer, {}/{}", vertices.size(), maxVertices);
VERIFY(elements.size() <= maxElements, "model elements too big for buffer, {}/{}", elements.size(), maxElements);
// Create a new batch if the quad limit has been reached
if (m_vertexIndex + vertices.size() > maxVertices || m_elementIndex + indices.size() > maxElements) {
if (m_vertexIndex + vertices.size() > maxVertices || m_elementIndex + elements.size() > maxElements) {
nextBatch();
}
uint32_t textureUnitIndex = addTextureUnit(texture);
// Add the vertices
for (auto vertex : 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 = (float)textureUnitIndex;
m_vertexBufferPtr->textureIndex = textureUnitIndex;
m_vertexBufferPtr++;
}
std::copy(indices.begin(), indices.end(), m_elementBufferPtr);
m_elementBufferPtr += indices.size();
m_vertexIndex += vertices.size();
m_elementIndex += indices.size();
// Copy element indices to the element buffer
for (const auto& element : elements) {
// Indices are referenced relative to vertices[0], if there are multiple models in a batch,
// then the indices need to be offset by the total amount of vertices
*m_elementBufferPtr++ = element + m_vertexIndex;
}
void Renderer3D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
{
m_shader->bind();
m_shader->setFloat("u_projectionView", cameraProjection * cameraView);
m_shader->unbind();
m_vertexIndex += vertices.size();
m_elementIndex += elements.size();
}
void Renderer3D::createElementBuffer()
{
// CPU
// ---------------------------------
// 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
// ---------------------------------
// 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()
@@ -567,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
+83 -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 };
};
// -------------------------------------
@@ -62,6 +64,8 @@ struct Vertex {
template<typename T>
class Renderer {
public:
static constexpr const uint32_t vertexPerFace = 3;
static constexpr const uint32_t elementPerFace = 3;
static constexpr const uint32_t vertexPerQuad = 4;
static constexpr const uint32_t elementPerQuad = 6;
@@ -74,6 +78,10 @@ public:
virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView);
virtual void endScene();
void setEnableDepthBuffer(bool state) { m_enableDepthBuffer = state; }
uint32_t shaderID() const { return m_shader->id(); }
protected:
Renderer() {}
virtual ~Renderer() { destroy(); };
@@ -97,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;
};
@@ -119,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:
@@ -151,7 +163,7 @@ public:
public:
RendererCubemap(s);
virtual ~RendererCubemap() = default;
virtual ~RendererCubemap();
using Singleton<RendererCubemap>::destroy;
@@ -160,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];
@@ -174,7 +191,7 @@ class RendererFont final
, public ruc::Singleton<RendererFont> {
public:
RendererFont(s);
virtual ~RendererFont() = default;
virtual ~RendererFont();
using Singleton<RendererFont>::destroy;
@@ -191,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;
@@ -207,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; };
+166 -10
View File
@@ -4,50 +4,206 @@
* 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
framebufferSetup(m_framebuffer);
renderGeometry();
framebufferTeardown(m_framebuffer);
// ---------------------------------
// Forward rendering to the screen
framebufferSetup(m_screenFramebuffer);
renderSkybox();
// Render 3D geometry post-processing
RendererPostProcess::the().drawQuad(transformIdentity, m_framebuffer->texture(0), m_framebuffer->texture(1), m_framebuffer->texture(2));
RendererPostProcess::the().endScene();
// Visual representation of light sources
Framebuffer::copyBuffer(m_framebuffer, m_screenFramebuffer, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
renderLightCubes();
// Render 2D, UI
renderOverlay();
framebufferTeardown(m_screenFramebuffer);
}
auto cubemapView = m_registry->view<TransformComponent, CubemapComponent>();
for (auto [entity, transform, cubemap] : cubemapView.each()) {
RendererCubemap::the().drawCubemap(transform, cubemap.color, cubemap.texture);
void RenderSystem::resize(int32_t width, int32_t height)
{
RenderCommand::setViewport(0, 0, width, height);
m_framebuffer->resize(width, height);
m_screenFramebuffer->resize(width, height);
}
// -----------------------------------------
void RenderSystem::framebufferSetup(std::shared_ptr<Framebuffer> framebuffer)
{
framebuffer->bind();
RenderCommand::setColorAttachmentCount(framebuffer->colorAttachmentCount());
RenderCommand::clearColor(framebuffer->clearColor());
RenderCommand::clearBit(framebuffer->clearBit());
}
void RenderSystem::framebufferTeardown(std::shared_ptr<Framebuffer> framebuffer)
{
framebuffer->unbind();
RenderCommand::setColorAttachmentCount(1);
}
void RenderSystem::renderGeometry()
{
auto [projection, view] = CameraSystem::the().projectionView();
auto translate = CameraSystem::the().translate();
Uniformbuffer::the().setValue("Camera", "u_projectionView", projection * view);
Uniformbuffer::the().setValue("Camera", "u_position", translate);
static DirectionalLightBlock directionalLights[2] = {
{
.direction = { -8.0f, -8.0f, -8.0f },
.ambient = { 0.1f, 0.1f, 0.1f },
.diffuse = { 1.0f, 1.0f, 1.0f },
.specular = { 1.0f, 1.0f, 1.0f },
},
{
.direction = { 8.0f, 8.0f, 8.0f },
.ambient = { 0.1f, 0.1f, 0.1f },
.diffuse = { 1.0f, 1.0f, 1.0f },
.specular = { 1.0f, 0.0f, 0.0f },
},
};
ShaderStorageBuffer::the().setValue("DirectionalLights", "u_directionalLight", directionalLights);
auto modelView = m_registry->view<TransformComponent, ModelComponent>();
for (auto [entity, transform, model] : modelView.each()) {
Renderer3D::the().drawModel(model.model->vertices(),
model.model->elements(),
transform,
model.color,
model.model->texture() ? model.model->texture() : model.texture);
}
Renderer3D::the().endScene();
}
void RenderSystem::renderSkybox()
{
auto [projection, view] = CameraSystem::the().projectionView();
RendererCubemap::the().beginScene(projection, view); // camera, lights, environment
auto cubemapView = m_registry->view<TransformComponent, CubemapComponent>();
for (auto [entity, transform, cubemap] : cubemapView.each()) {
if (!cubemap.isLight) {
RendererCubemap::the().drawCubemap(transform, cubemap.color, cubemap.texture);
}
}
RendererCubemap::the().endScene();
}
void RenderSystem::renderLightCubes()
{
auto [projection, view] = CameraSystem::the().projectionView();
RendererLightCube::the().beginScene(projection, view); // camera, lights, environment
auto cubemapView = m_registry->view<TransformComponent, CubemapComponent>();
for (auto [entity, transform, cubemap] : cubemapView.each()) {
if (cubemap.isLight) {
RendererLightCube::the().drawCubemap(transform, cubemap.color, nullptr);
}
}
RendererLightCube::the().endScene();
}
void RenderSystem::renderOverlay()
{
auto quadView = m_registry->view<TransformComponent, SpriteComponent>();
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
+17 -3
View File
@@ -6,25 +6,39 @@
#pragma once
#include <cstdint> // int32_t, uint32_t
#include <memory> //std::shared_ptr
#include "entt/entity/registry.hpp" // entt::entity, entt::registry
#include "entt/entity/fwd.hpp" // entt::registry
#include "ruc/singleton.h"
#include "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