System info & network

This commit is contained in:
2023-09-26 19:40:16 +02:00
commit 504ba77654
89 changed files with 39577 additions and 0 deletions

382
code/render/2d.cpp Normal file
View File

@@ -0,0 +1,382 @@
#include "2d.h"
#include "state.h"
#include "../debug/logger.h"
static const u32 PIXELS_PER_SEGMENT = 3;
// Internal use functions
static void set_texture_in_shader(r_shader *shader, r_texture *texture, v4 color = {1,1,1,1}, u32 texture_index = 0);
// Immediate functions
void r_2d_immediate_segment(v2 a, v2 b, v4 a_color, v4 b_color, f32 thickness)
{
glDisable(GL_DEPTH_TEST);
// Shader
glUseProgram(r_render_state.shader_2d.id);
glUniform1i(r_render_state.shader_2d.has_texture[0], 0);
// Vertex buffer data
GLuint gl_VAO, gl_VBO;
glGenVertexArrays(1, &gl_VAO);
glBindVertexArray(gl_VAO);
glGenBuffers(1, &gl_VBO);
glBindBuffer(GL_ARRAY_BUFFER, gl_VBO);
f32 data[12] = { a.x, a.y, b.x, b.y, a_color.r, a_color.g, a_color.b, a_color.a, b_color.r, b_color.g, b_color.b, b_color.a };
glBufferData(GL_ARRAY_BUFFER, 2*sizeof(v2)+2*sizeof(v4), data, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(v4), (void*)(2*sizeof(v2)));
// Draw
glLineWidth(thickness);
glDrawArrays(GL_LINES, 0, 2);
// Deinit
glDeleteBuffers(1, &gl_VBO);
glDeleteVertexArrays(1, &gl_VAO);
glEnable(GL_DEPTH_TEST);
}
void r_2d_immediate_polygonal_chain(u64 count, v2 *vertices, v4 color, f32 thickness)
{
glDisable(GL_DEPTH_TEST);
// Shader
glUseProgram(r_render_state.shader_2d.id);
glUniform1i(r_render_state.shader_2d.has_texture[0], 0);
// Vertex buffer data
GLuint gl_VAO, gl_VBO;
glGenVertexArrays(1, &gl_VAO);
glBindVertexArray(gl_VAO);
glGenBuffers(1, &gl_VBO);
glBindBuffer(GL_ARRAY_BUFFER, gl_VBO);
glBufferData(GL_ARRAY_BUFFER, count*sizeof(v2), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0);
//glDisableVertexAttribArray(1);
glVertexAttrib4f(1, color.r, color.g, color.b, color.a);
// Draw
glLineWidth(thickness);
glDrawArrays(GL_LINE_STRIP, 0, count);
// Deinit
glDeleteBuffers(1, &gl_VBO);
glDeleteVertexArrays(1, &gl_VAO);
glEnable(GL_DEPTH_TEST);
}
void r_2d_immediate_triangle(v2 a, v2 b, v2 c, v4 a_color, v4 b_color, v4 c_color, v2 a_uv, v2 b_uv, v2 c_uv, r_texture *texture)
{
glDisable(GL_DEPTH_TEST);
// Shader
glUseProgram(r_render_state.shader_2d.id);
// Texture
set_texture_in_shader(&r_render_state.shader_2d, texture);
// Vertex buffer data
GLuint gl_VAO, gl_VBO;
glGenVertexArrays(1, &gl_VAO);
glBindVertexArray(gl_VAO);
glGenBuffers(1, &gl_VBO);
glBindBuffer(GL_ARRAY_BUFFER, gl_VBO);
f32 data[24] = {
a.x, a.y, b.x, b.y, c.x, c.y,
a_color.r, a_color.g, a_color.b, a_color.a,
b_color.r, b_color.g, b_color.b, b_color.a,
c_color.r, c_color.g, c_color.b, c_color.a,
a_uv.x, a_uv.y, b_uv.x, b_uv.y, c_uv.x, c_uv.y
};
glBufferData(GL_ARRAY_BUFFER, 3*sizeof(v2)+3*sizeof(v4)+3*sizeof(v2), data, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(v4), (void*)(3*sizeof(v2)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)(3*sizeof(v2)+3*sizeof(v4)));
// Draw
glDrawArrays(GL_TRIANGLES, 0, 3);
// Deinit
glDeleteBuffers(1, &gl_VBO);
glDeleteVertexArrays(1, &gl_VAO);
glEnable(GL_DEPTH_TEST);
}
void r_2d_immediate_quad(v2 a, v2 b, v2 c, v2 d, v4 color, v2 a_uv, v2 b_uv, v2 c_uv, v2 d_uv, r_texture *texture)
{
glDisable(GL_DEPTH_TEST);
// Shader
glUseProgram(r_render_state.shader_2d.id);
// Texture
set_texture_in_shader(&r_render_state.shader_2d, texture);
// Vertex buffer data
GLuint gl_VAO, gl_VBO;
glGenVertexArrays(1, &gl_VAO);
glBindVertexArray(gl_VAO);
glGenBuffers(1, &gl_VBO);
glBindBuffer(GL_ARRAY_BUFFER, gl_VBO);
f32 data[16] = {
a.x, a.y, b.x, b.y, d.x, d.y, c.x, c.y,
a_uv.x, a_uv.y, b_uv.x, b_uv.y, d_uv.x, d_uv.y, c_uv.x, c_uv.y
};
glBufferData(GL_ARRAY_BUFFER, 4*sizeof(v2)+4*sizeof(v2), data, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0);
//glDisableVertexAttribArray(1);
glVertexAttrib4f(1, color.r, color.g, color.b, color.a);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)(4*sizeof(v2)));
// Draw
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Deinit
glDeleteBuffers(1, &gl_VBO);
glDeleteVertexArrays(1, &gl_VAO);
glEnable(GL_DEPTH_TEST);
}
void r_2d_immediate_rectangle(Rect r, v4 color, Rect uv, r_texture *texture)
{
v2 a = r.position + v2{r.w, 0 };
v2 b = r.position + v2{0 , 0 };
v2 c = r.position + v2{0 , r.h};
v2 d = r.position + v2{r.w, r.h};
v2 a_uv = uv.position + v2{uv.w, 0 };
v2 b_uv = uv.position + v2{0 , 0 };
v2 c_uv = uv.position + v2{0 , uv.h};
v2 d_uv = uv.position + v2{uv.w, uv.h};
r_2d_immediate_quad(a, b, c, d, color, a_uv, b_uv, c_uv, d_uv, texture);
}
void r_2d_immediate_rounded_rectangle(Rect r, f32 radius, v4 color)
{
radius = clamp(0, minimum(abs(r.w), abs(r.h)) / 2, radius);
// Should compute PIXELS_PER_SEGMENT from the size of the radius
u32 num_of_segments = floor(0.5 + radius * TAU * 0.25 / PIXELS_PER_SEGMENT);
// Split into 9 sections:
// - 5 quads (center, top, bottom, left, right)
// - 4 semicircles (corners)
// Inner vertices (CCW starting from 1st quadrant/upper-right)
v2 i0, i1, i2, i3;
i0.x = r.x + r.w - radius;
i0.y = r.y + radius;
i1.x = r.x + radius;
i1.y = r.y + radius;
i2.x = r.x + radius;
i2.y = r.y + r.h - radius;
i3.x = r.x + r.w - radius;
i3.y = r.y + r.h - radius;
// Outer vertices
v2 o0, o1, o2, o3, o4, o5, o6, o7;
o0.x = i0.x;
o0.y = i0.y - radius;
o1.x = i1.x;
o1.y = i1.y - radius;
o2.x = i1.x - radius;
o2.y = i1.y;
o3.x = i2.x - radius;
o3.y = i2.y;
o4.x = i2.x;
o4.y = i2.y + radius;
o5.x = i3.x;
o5.y = i3.y + radius;
o6.x = i3.x + radius;
o6.y = i3.y;
o7.x = i0.x + radius;
o7.y = i0.y;
// Reserve space for vertices
u32 vertices_count = 30; // 5 quads, specified by 2 triangles each = 5 quads * 2 triangles * 3 vertices = 30 vertices
vertices_count += num_of_segments * 12; // Add corner semicircles = 4 corners * N triangles * 3 vertices = N * 12
// Build 5 quads
v2 vertices[vertices_count] = {
i0, i1, i2, i0, i2, i3, // Center quad: i0, i1, i2, i3
o0, o1, i1, o0, i1, i0, // Top quad: o0, o1, i1, i0
i1, o2, o3, i1, o3, i2, // Left quad: i1, o2, o3, i2
i3, i2, o4, i3, o4, o5, // Bottom quad: i3, i2, o4, o5
o7, i0, i3, o7, i3, o6 // Right quad: o7, i0, i3, o6
};
u32 corner_offset = 30;
// Corner semicircles
f32 factor = TAU * .25 / num_of_segments;
v2 inner_vertices[4] = {i0, i1, i2, i3};
for(u32 quadrant = 0; quadrant < 4; quadrant++)
{
for(u32 i = quadrant*num_of_segments; i < (quadrant+1)*num_of_segments; i++)
{
v2 inner = inner_vertices[quadrant];
v2 a = inner + radius * v2{cos( i * factor), -sin( i * factor)};
v2 b = inner + radius * v2{cos((i+1) * factor), -sin((i+1) * factor)};
vertices[corner_offset + 3*i + 0] = inner;
vertices[corner_offset + 3*i + 1] = a;
vertices[corner_offset + 3*i + 2] = b;
}
}
r_2d_immediate_mesh(vertices_count, vertices, color, NULL, NULL);
}
void r_2d_immediate_mesh(u64 count, v2 *vertices, v4 color, v2 *uvs, r_texture *texture)
{
glDisable(GL_DEPTH_TEST);
// Shader
glUseProgram(r_render_state.shader_2d.id);
// Texture
set_texture_in_shader(&r_render_state.shader_2d, texture);
// Vertex buffer data
GLuint gl_VAO, gl_vertices, gl_uvs;
glGenVertexArrays(1, &gl_VAO);
glBindVertexArray(gl_VAO);
glGenBuffers(1, &gl_vertices);
glGenBuffers(1, &gl_uvs);
glBindBuffer(GL_ARRAY_BUFFER, gl_vertices);
glBufferData(GL_ARRAY_BUFFER, count * sizeof(v2), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0);
//glDisableVertexAttribArray(1);
glVertexAttrib4f(1, color.r, color.g, color.b, color.a);
glBindBuffer(GL_ARRAY_BUFFER, gl_uvs);
glBufferData(GL_ARRAY_BUFFER, count * sizeof(v2), uvs , GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0);
// Draw
glDrawArrays(GL_TRIANGLES, 0, count);
// Deinitvoid r_2d_draw_mesh(r_2d_mesh *mesh, r_texture *texture);
glDeleteBuffers(1, &gl_vertices);
glDeleteBuffers(1, &gl_uvs);
glDeleteVertexArrays(1, &gl_VAO);
glEnable(GL_DEPTH_TEST);
}
void r_2d_immediate_rectangle_outline(Rect r, v4 color, f32 thickness)
{
v2 vertices[5];
vertices[0] = r.position + v2{ 0, 0};
vertices[1] = r.position + v2{ 0, r.h};
vertices[2] = r.position + v2{r.w, r.h};
vertices[3] = r.position + v2{r.w, 0};
vertices[4] = r.position + v2{ 0, 0};
r_2d_immediate_polygonal_chain(5, vertices, color, thickness);
}
void r_2d_immediate_rounded_rectangle_outline(Rect r, f32 radius, v4 color, f32 thickness)
{
radius = clamp(0, minimum(abs(r.w), abs(r.h)) / 2, radius);
// Should compute PIXELS_PER_SEGMENT from the size of the radius
u32 num_of_segments = floor(0.5 + radius * TAU * 0.25 / PIXELS_PER_SEGMENT);
// Split into 9 sections:
// - 5 quads (center, top, bottom, left, right)
// - 4 semicircles (corners)
// Inner vertices (CCW starting from 1st quadrant/upper-right)
v2 i0, i1, i2, i3;
i0.x = r.x + r.w - radius;
i0.y = r.y + radius;
i1.x = r.x + radius;
i1.y = r.y + radius;
i2.x = r.x + radius;
i2.y = r.y + r.h - radius;
i3.x = r.x + r.w - radius;
i3.y = r.y + r.h - radius;
// Reserve space for vertices
u32 vertices_count = 1 + 4 + 4*num_of_segments; // Starting vertex (1) + one for each side (4) + one for each segment (4*num_of_segments)
v2 inner_vertices[4] = {i0, i1, i2, i3};
v2 vertices[vertices_count] = {
i3 + v2{radius, 0} // Starting vertex
};
u32 v_index = 1;
// Corner semicircles
f32 factor = TAU * .25 / num_of_segments;
for(u32 quadrant = 0; quadrant < 4; quadrant++)
{
v2 inner = inner_vertices[quadrant];
for(u32 i = quadrant*num_of_segments; i < (quadrant+1)*num_of_segments; i++)
{
v2 a = inner + radius * v2{cos( i * factor), -sin( i * factor)};
vertices[v_index] = a;
v_index++;
}
vertices[v_index] = inner + radius * v2{cos( (quadrant+1)*num_of_segments * factor), -sin( (quadrant+1)*num_of_segments * factor)};
v_index++;
}
r_2d_immediate_polygonal_chain(vertices_count, vertices, color, thickness);
}
void r_2d_draw_mesh(r_2d_mesh *mesh, r_texture *texture)
{
glDisable(GL_DEPTH_TEST);
// Shader
glUseProgram(r_render_state.shader_2d.id);
// Texture
set_texture_in_shader(&r_render_state.shader_2d, texture);
// Draw
glBindVertexArray(mesh->gl_VAO);
glDrawArrays(GL_TRIANGLES, 0, mesh->count);
glEnable(GL_DEPTH_TEST);
}
// Internal use functions
static void set_texture_in_shader(r_shader *shader, r_texture *texture, v4 color, u32 texture_index)
{
// Remember to call glUseProgram before using this
if(texture)
{
glUniform1i(shader->has_texture[texture_index], 1);
glUniform1i(shader->texture[texture_index], 0);
glUniform1i(shader->texture_channels[texture_index], r_texture_channels(texture));
glActiveTexture(GL_TEXTURE0 + texture_index);
glBindTexture(GL_TEXTURE_2D, texture->gl_id);
glUniform4f(shader->color[texture_index], color.r, color.g, color.b, color.a);
}
else
{
glUniform1i(shader->has_texture[texture_index], 0);
}
}

27
code/render/2d.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef _PIUMA_RENDER_2D_H_
#define _PIUMA_RENDER_2D_H_
#include "../lib/types.h"
#include "../lib/math.h"
#include "../lib/geometry.h"
#include "primitives.h"
// Immediate functions: less efficient but easy to use
void r_2d_immediate_segment(v2 a, v2 b, v4 a_color, v4 b_color, f32 thickness = 1.0f);
void r_2d_immediate_polygonal_chain(u64 count, v2 *vertices, v4 color, f32 thickness = 1.0f);
void r_2d_immediate_triangle(v2 a, v2 b, v2 c, v4 a_color, v4 b_color, v4 c_color, v2 a_uv = {0,0}, v2 b_uv = {0,0}, v2 c_uv = {0,0}, r_texture *texture = NULL);
void r_2d_immediate_quad(v2 a, v2 b, v2 c, v2 d, v4 color, v2 a_uv = {0,0}, v2 b_uv = {0,0}, v2 c_uv = {0,0}, v2 d_uv = {0,0}, r_texture *texture = NULL);
void r_2d_immediate_rectangle(Rect r, v4 color, Rect uv = {0,0,1,1}, r_texture *texture = NULL);
void r_2d_immediate_rounded_rectangle(Rect r, f32 radius, v4 color);
void r_2d_immediate_mesh(u64 count, v2 *vertices, v4 color, v2 *uvs = NULL, r_texture *texture = NULL);
void r_2d_immediate_rectangle_outline(Rect r, v4 color, f32 thickness = 1.0f);
void r_2d_immediate_rounded_rectangle_outline(Rect r, f32 radius, v4 color, f32 thickness = 1.0f);
// Draw functions: usual interface (create objects / send to gpu, draw call, remove from gpu)
void r_2d_draw_mesh(r_2d_mesh *mesh, r_texture *texture);
// @Feature: @Performance: something for text rendering (a lot of quads/rects)
// Maybe we could send to the gpu the following: texture of the characters, array of uvs for indexing the texture, character position + index of its uv.
#endif

52
code/render/gl_helpers.h Normal file
View File

@@ -0,0 +1,52 @@
#ifndef _PIUMA_RENDER_GL_HELPERS_H_
#define _PIUMA_RENDER_GL_HELPERS_H_
#include "../debug/logger.h"
inline void APIENTRY glDebugOutput(GLenum source, GLenum type, unsigned int id, GLenum severity, GLsizei length, const char *message, const void *userParam)
{
// ignore non-significant error/warning codes
if(id == 131169 || id == 131185 || id == 131218 || id == 131204)
return;
if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
return;
// @Cleanup: LOG does not replace printf here. We need to merge the multiple printfs into a single log message
LOG(LOG_DEBUG, "---------------");
LOG(LOG_DEBUG, "Debug message (%u): %s", id, message);
switch (source)
{
case GL_DEBUG_SOURCE_API: LOG(LOG_DEBUG, "Source: API"); break;
case GL_DEBUG_SOURCE_WINDOW_SYSTEM: LOG(LOG_DEBUG, "Source: Window System"); break;
case GL_DEBUG_SOURCE_SHADER_COMPILER: LOG(LOG_DEBUG, "Source: Shader Compiler"); break;
case GL_DEBUG_SOURCE_THIRD_PARTY: LOG(LOG_DEBUG, "Source: Third Party"); break;
case GL_DEBUG_SOURCE_APPLICATION: LOG(LOG_DEBUG, "Source: Application"); break;
case GL_DEBUG_SOURCE_OTHER: LOG(LOG_DEBUG, "Source: Other"); break;
}
switch (type)
{
case GL_DEBUG_TYPE_ERROR: LOG(LOG_DEBUG, "Type: Error"); break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: LOG(LOG_DEBUG, "Type: Deprecated Behaviour"); break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: LOG(LOG_DEBUG, "Type: Undefined Behaviour"); break;
case GL_DEBUG_TYPE_PORTABILITY: LOG(LOG_DEBUG, "Type: Portability"); break;
case GL_DEBUG_TYPE_PERFORMANCE: LOG(LOG_DEBUG, "Type: Performance"); break;
case GL_DEBUG_TYPE_MARKER: LOG(LOG_DEBUG, "Type: Marker"); break;
case GL_DEBUG_TYPE_PUSH_GROUP: LOG(LOG_DEBUG, "Type: Push Group"); break;
case GL_DEBUG_TYPE_POP_GROUP: LOG(LOG_DEBUG, "Type: Pop Group"); break;
case GL_DEBUG_TYPE_OTHER: LOG(LOG_DEBUG, "Type: Other"); break;
}
switch (severity)
{
case GL_DEBUG_SEVERITY_HIGH: LOG(LOG_DEBUG, "Severity: high"); break;
case GL_DEBUG_SEVERITY_MEDIUM: LOG(LOG_DEBUG, "Severity: medium"); break;
case GL_DEBUG_SEVERITY_LOW: LOG(LOG_DEBUG, "Severity: low"); break;
case GL_DEBUG_SEVERITY_NOTIFICATION: LOG(LOG_DEBUG, "Severity: notification"); break;
}
LOG(LOG_DEBUG, "");
}
#endif

0
code/render/lights.cpp Normal file
View File

44
code/render/lights.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef _PIUMA_RENDER_LIGHTS_H_
#define _PIUMA_RENDER_LIGHTS_H_
struct r_sun_light
{
v3 direction;
f32 _padding0;
v3 color;
f32 intensity;
};
struct r_point_light
{
v3 position;
f32 _padding0;
v3 color;
f32 intensity;
};
struct r_spot_light
{
v3 position;
f32 inner_radius;
v3 color;
f32 intensity;
v3 direction;
f32 outer_radius;
};
#define MAX_SUN_LIGHTS 4
#define MAX_POINT_LIGHTS 128
#define MAX_SPOT_LIGHTS 128
struct r_light_container
{
u32 sun_light_count;
u32 point_light_count;
u32 spot_light_count;
f32 ambient_light;
r_sun_light sun_lights[MAX_SUN_LIGHTS];
r_point_light point_lights[MAX_POINT_LIGHTS];
r_spot_light spot_lights[MAX_SPOT_LIGHTS];
};
#endif

79
code/render/object.cpp Normal file
View File

@@ -0,0 +1,79 @@
#include "object.h"
#include "../lib/math.h"
#include "../lib/geometry.h"
#include "../platform.h"
#include "string.h"
#include "state.h"
#include "2d.h"
r_object r_object_create(u64 count, r_mesh **meshes, r_material **materials, v3 position, v3 rotation, v3 scale)
{
r_object object;
object.meshes = (r_mesh**)p_alloc(count * sizeof(r_mesh*));
object.mesh_material = (r_material**)p_alloc(count * sizeof(r_material*));
object.mesh_local_transform = (m4*)p_alloc(count * sizeof(m4));
memcpy(object.meshes, meshes, count * sizeof(r_mesh*));
memcpy(object.mesh_material, materials, count * sizeof(r_material*));
for(u64 i = 0; i < count; i++)
object.mesh_local_transform[i] = m4_identity;
object.count = count;
object.position = position;
object.rotation = rotation;
object.scale = scale;
object.has_shadow = true;
return object;
}
r_object r_object_allocate(u64 count)
{
r_object object;
object.meshes = (r_mesh**)p_alloc(count * sizeof(r_mesh*));
object.mesh_material = (r_material**)p_alloc(count * sizeof(r_material*));
object.mesh_local_transform = (m4*)p_alloc(count * sizeof(m4));
for(u64 i = 0; i < count; i++)
object.mesh_local_transform[i] = m4_identity;
object.count = count;
object.position = v3{0,0,0};
object.rotation = v3{0,0,0};
object.scale = v3{1,1,1};
object.has_shadow = true;
return object;
}
void r_object_destroy(r_object *object)
{
p_free(object->mesh_material);
p_free(object->meshes);
p_free(object->mesh_local_transform);
object->mesh_material = NULL;
object->meshes = NULL;
object->mesh_local_transform = NULL;
object->count = 0;
}
m4 r_object_transform_matrix(r_object *object)
{
// @Performance: replace with rototranslation matrix
return translation_v3(object->position) * rotation_v3(object->rotation) * scale_v3(object->scale);
}
m4 r_object_mesh_transform_matrix(r_object *object, u64 mesh_i)
{
// @Performance: replace with rototranslation matrix
if(object->mesh_local_transform)
return r_object_transform_matrix(object) * object->mesh_local_transform[mesh_i];
return r_object_transform_matrix(object);
}

64
code/render/object.h Normal file
View File

@@ -0,0 +1,64 @@
#ifndef _PIUMA_RENDER_OBJECT_H_
#define _PIUMA_RENDER_OBJECT_H_
#include "primitives.h"
#include "shader.h"
struct r_material
{
// @Feature: PBR materials
r_shader *shader;
r_texture *albedo_texture;
v4 albedo_factor;
r_texture *metallic_texture;
f32 metallic_factor;
r_texture *roughness_texture;
f32 roughness_factor;
r_texture *normal_texture;
r_texture *emissive_texture;
v4 emissive_factor;
/*
albedo |
metallic | --> Disney + UE4 paper
roughness / specular |
cavity |
subsurface |
anisotropy | --> Disney paper
clearcoat |
sheen |
ambient occlusion | --> Valve Source2, same as cavity?
emission
normals
*/
};
struct r_object
{
// @Feature: actually support multiple meshes/materials in the implementation code
r_mesh **meshes;
r_material **mesh_material;
m4 *mesh_local_transform;
u64 count;
v3 scale;
v3 position;
v3 rotation;
bool has_shadow;
};
r_object r_object_create(u64 count, r_mesh **meshes, r_material **materials, v3 position = {0,0,0}, v3 rotation = {0,0,0}, v3 scale = {1,1,1});
r_object r_object_allocate(u64 count);
void r_object_destroy(r_object *object);
m4 r_object_transform_matrix(r_object *object);
m4 r_object_mesh_transform_matrix(r_object *object, u64 mesh_i);
#endif

424
code/render/primitives.cpp Normal file
View File

@@ -0,0 +1,424 @@
#include "primitives.h"
#include "../platform.h"
#include <string.h>
// GL utils
static GLint gl_texture_internal_format(u32 texture_flags);
static GLenum gl_texture_format (u32 texture_flags);
static GLenum gl_texture_type (u32 texture_flags);
// Texture
r_texture r_texture_create(u8 *data, v2s size, u32 flags)
{
r_texture texture;
texture.data = data;
texture.size = size;
texture.flags = flags | R_TEXTURE_INITIALIZED;
glGenTextures(1, &texture.gl_id);
glBindTexture(GL_TEXTURE_2D, texture.gl_id);
GLint internal_format = gl_texture_internal_format(flags);
GLenum format = gl_texture_format (flags);
GLenum type = gl_texture_type (flags);
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size.x, size.y, 0, format, type, data);
if(flags & R_TEXTURE_NO_MIPMAP)
{
// The default for GL_TEXTURE_{MIN,MAG}_FILTER is GL_NEAREST_MIPMAP_LINEAR, but we are not using mipmaps for this texture. If we don't change this parameter, the image will not render.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
else
{
glGenerateMipmap(GL_TEXTURE_2D);
}
if(flags & R_TEXTURE_DONT_OWN)
texture.data = NULL;
return texture;
}
void r_texture_resize(r_texture *texture, v2s size)
{
glBindTexture(GL_TEXTURE_2D, texture->gl_id);
GLint internal_format = gl_texture_internal_format(texture->flags);
GLenum format = gl_texture_format (texture->flags);
GLenum type = gl_texture_type (texture->flags);
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size.x, size.y, 0, format, type, NULL);
texture->size = size;
}
void r_texture_update(r_texture *texture, u8 *data, v2s size, v2s position, u32 stride)
{
glBindTexture(GL_TEXTURE_2D, texture->gl_id);
GLenum format = gl_texture_format(texture->flags);
GLenum type = gl_texture_type (texture->flags);
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
glTexSubImage2D(GL_TEXTURE_2D, 0, position.x, position.y, size.x, size.y, format, type, data);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
if(texture->flags & R_TEXTURE_NO_MIPMAP)
{
// The default for GL_TEXTURE_{MIN,MAG}_FILTER is GL_NEAREST_MIPMAP_LINEAR, but we are not using mipmaps for this texture. If we don't change this parameter, the image will not render.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
else
{
glGenerateMipmap(GL_TEXTURE_2D);
}
}
void r_texture_destroy(r_texture *texture)
{
glDeleteTextures(1, &texture->gl_id);
if(texture->data && !(texture->flags & R_TEXTURE_DONT_OWN))
p_free(texture->data);
texture->size = {0,0};
texture->flags = R_TEXTURE_DESTROYED;
}
s32 r_texture_channels(r_texture *texture)
{
if(texture->flags & R_TEXTURE_ALPHA)
return 1;
if(texture->flags & R_TEXTURE_RGB)
return 3;
if(texture->flags & (R_TEXTURE_RGBA | R_TEXTURE_SRGB))
return 4;
return 4;
}
// Cubemap
r_cubemap r_cubemap_create(float *data[6], v2s size, u32 flags)
{
r_cubemap cubemap;
for(u32 i = 0; i < 6; i++)
cubemap.data[i] = data[i];
cubemap.size = size;
cubemap.flags = flags | R_CUBEMAP_INITIALIZED;
glGenTextures(1, &cubemap.gl_id);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.gl_id);
for(u32 i = 0; i < 6; i++)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size.x, size.y, 0, GL_RGB, GL_FLOAT, data[i]);
if(flags & R_CUBEMAP_DONT_OWN)
cubemap.data[i] = NULL;
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
return cubemap;
}
void r_cubemap_destroy(r_cubemap *cubemap)
{
glDeleteTextures(1, &cubemap->gl_id);
for(u32 i = 0; i < 6; i++)
{
if(cubemap->data[i] && !(cubemap->flags & R_CUBEMAP_DONT_OWN))
p_free(cubemap->data[i]);
}
cubemap->size = {0,0};
cubemap->flags = R_CUBEMAP_DESTROYED;
}
// 2D mesh
r_2d_mesh r_2d_mesh_create(u64 count, v2 *vertices, v4 *colors, v2 *uvs)
{
r_2d_mesh mesh;
mesh.vertices = vertices;
mesh.colors = colors;
mesh.uvs = uvs;
mesh.count = count;
glGenVertexArrays(1, &mesh.gl_VAO);
glBindVertexArray(mesh.gl_VAO);
glGenBuffers(1, &mesh.gl_vertex_buffer);
glGenBuffers(1, &mesh.gl_color_buffer);
glGenBuffers(1, &mesh.gl_uv_buffer);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.count * sizeof(v2), mesh.vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_color_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.count * sizeof(v4), mesh.colors, GL_STATIC_DRAW);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(v4), (void*)0);
if(mesh.uvs)
{
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_uv_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.count * sizeof(v2), mesh.uvs, GL_STATIC_DRAW);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0);
}
return mesh;
}
void r_2d_mesh_destroy(r_2d_mesh *mesh)
{
glBindVertexArray(mesh->gl_VAO);
glDeleteBuffers(1, &mesh->gl_vertex_buffer);
glDeleteBuffers(1, &mesh->gl_color_buffer);
glDeleteBuffers(1, &mesh->gl_uv_buffer);
glDeleteVertexArrays(1, &mesh->gl_VAO);
if(mesh->vertices)
p_free(mesh->vertices);
if(mesh->colors)
p_free(mesh->colors);
if(mesh->uvs)
p_free(mesh->uvs);
}
// 3D mesh
r_mesh r_mesh_create(u64 indices_count, u32 *indices, u64 vertices_count, v3 *vertices, v3 *normals, v3 *tangents, v2 *uvs)
{
r_mesh mesh;
mesh.vertices = vertices;
mesh.normals = normals;
mesh.tangents = tangents;
mesh.uvs = uvs;
mesh.vertices_count = vertices_count;
mesh.indices = indices;
mesh.indices_count = indices_count;
glGenVertexArrays(1, &mesh.gl_VAO);
glBindVertexArray(mesh.gl_VAO);
glGenBuffers(1, &mesh.gl_vertex_buffer);
glGenBuffers(1, &mesh.gl_normal_buffer);
glGenBuffers(1, &mesh.gl_tangent_buffer);
glGenBuffers(1, &mesh.gl_uv_buffer);
glGenBuffers(1, &mesh.gl_index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.gl_index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indices_count * sizeof(u32), mesh.indices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices_count * sizeof(v3), mesh.vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(v3), (void*)0);
if(mesh.normals)
{
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_normal_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices_count * sizeof(v3), mesh.normals, GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(v3), (void*)0);
}
if(mesh.tangents)
{
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_tangent_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices_count * sizeof(v3), mesh.tangents, GL_STATIC_DRAW);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(v3), (void*)0);
}
if(mesh.uvs)
{
glEnableVertexAttribArray(3);
glBindBuffer(GL_ARRAY_BUFFER, mesh.gl_uv_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices_count * sizeof(v2), mesh.uvs, GL_STATIC_DRAW);
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0);
}
return mesh;
}
void r_mesh_destroy(r_mesh *mesh)
{
glBindVertexArray(mesh->gl_VAO);
glDeleteBuffers(1, &mesh->gl_vertex_buffer);
glDeleteBuffers(1, &mesh->gl_normal_buffer);
glDeleteBuffers(1, &mesh->gl_uv_buffer);
glDeleteBuffers(1, &mesh->gl_index_buffer);
glDeleteVertexArrays(1, &mesh->gl_VAO);
if(mesh->vertices)
p_free(mesh->vertices);
if(mesh->normals)
p_free(mesh->normals);
if(mesh->uvs)
p_free(mesh->uvs);
if(mesh->indices)
p_free(mesh->indices);
}
// Common meshes
r_mesh r_mesh_build_cube()
{
v3 v[24] = {
{0.5, 0.5, -0.5},
{0.5, 0.5, -0.5},
{0.5, 0.5, -0.5},
{0.5, -0.5, -0.5},
{0.5, -0.5, -0.5},
{0.5, -0.5, -0.5},
{0.5, 0.5, 0.5},
{0.5, 0.5, 0.5},
{0.5, 0.5, 0.5},
{0.5, -0.5, 0.5},
{0.5, -0.5, 0.5},
{0.5, -0.5, 0.5},
{-0.5, 0.5, -0.5},
{-0.5, 0.5, -0.5},
{-0.5, 0.5, -0.5},
{-0.5, -0.5, -0.5},
{-0.5, -0.5, -0.5},
{-0.5, -0.5, -0.5},
{-0.5, 0.5, 0.5},
{-0.5, 0.5, 0.5},
{-0.5, 0.5, 0.5},
{-0.5, -0.5, 0.5},
{-0.5, -0.5, 0.5},
{-0.5, -0.5, 0.5}
};
v3 n[24] = {
{0.0, 1.0, 0.0},
{1.0, 0.0, 0.0},
{0.0, 0.0, -1.0},
{0.0, -1.0, 0.0},
{1.0, 0.0, 0.0},
{0.0, 0.0, -1.0},
{0.0, 1.0, 0.0},
{0.0, 0.0, 1.0},
{1.0, 0.0, 0.0},
{0.0, 0.0, 1.0},
{0.0, -1.0, 0.0},
{1.0, 0.0, 0.0},
{0.0, 1.0, 0.0},
{-1.0, 0.0, 0.0},
{0.0, 0.0, -1.0},
{-1.0, 0.0, 0.0},
{0.0, -1.0, 0.0},
{0.0, 0.0, -1.0},
{0.0, 1.0, 0.0},
{0.0, 0.0, 1.0},
{-1.0, 0.0, 0.0},
{0.0, 0.0, 1.0},
{-1.0, 0.0, 0.0},
{0.0, -1.0, 0.0}
};
u32 i[36] = {12, 6, 0, 7, 21, 9, 20, 15, 22, 3, 23, 16, 1, 11, 4, 14, 5, 17, 12, 18, 6, 7, 19, 21, 20, 13, 15, 3, 10, 23, 1, 8, 11, 14, 2, 5};
v3 *vertices = (v3*)p_alloc(24 * sizeof(v3));
v3 *normals = (v3*)p_alloc(24 * sizeof(v3));
u32 *indices = (u32*)p_alloc(36 * sizeof(u32));
memcpy(vertices, v, 24 * sizeof(v3));
memcpy(normals , n, 24 * sizeof(v3));
memcpy(indices , i, 36 * sizeof(u32));
return r_mesh_create(36, indices, 24, vertices, normals, NULL, NULL);
}
// Framebuffers
r_framebuffer r_framebuffer_create(v2s size, u32 flags)
{
r_framebuffer fb;
fb.flags = flags;
fb.size = size;
glGenFramebuffers(1, &fb.gl_id);
glBindFramebuffer(GL_FRAMEBUFFER, fb.gl_id);
fb.color_texture = r_texture_create(NULL, size, R_TEXTURE_RGBA | R_TEXTURE_HDR | R_TEXTURE_NO_MIPMAP);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.color_texture.gl_id, 0);
fb.gl_depth_id = 0;
if(flags & R_FRAMEBUFFER_DEPTH)
{
glGenRenderbuffers(1, &fb.gl_depth_id);
glBindRenderbuffer(GL_RENDERBUFFER, fb.gl_depth_id);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, size.x, size.y);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb.gl_depth_id);
}
return fb;
}
void r_framebuffer_destroy(r_framebuffer *fb)
{
r_texture_destroy(&fb->color_texture);
if(fb->flags & R_FRAMEBUFFER_DEPTH)
glDeleteRenderbuffers(1, &fb->gl_depth_id);
glDeleteFramebuffers(1, &fb->gl_id);
}
void r_framebuffer_update_size(r_framebuffer *fb, v2s size)
{
glBindFramebuffer(GL_FRAMEBUFFER, fb->gl_id);
r_texture_resize(&fb->color_texture, size);
if(fb->flags & R_FRAMEBUFFER_DEPTH)
{
glBindRenderbuffer(GL_RENDERBUFFER, fb->gl_depth_id);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, size.x, size.y);
}
fb->size = size;
}
// GL utils
static GLint gl_texture_internal_format(u32 texture_flags)
{
if(texture_flags & R_TEXTURE_ALPHA)
return GL_RED;
else if(texture_flags & R_TEXTURE_RGB)
return (texture_flags & R_TEXTURE_HDR) ? GL_RGB16F : GL_RGB;
else if(texture_flags & R_TEXTURE_RGBA)
return (texture_flags & R_TEXTURE_HDR) ? GL_RGBA16F : GL_RGBA;
else if(texture_flags & R_TEXTURE_SRGB)
return GL_SRGB_ALPHA;
return GL_RGBA;
}
static GLenum gl_texture_format(u32 texture_flags)
{
if(texture_flags & R_TEXTURE_ALPHA)
return GL_RED;
else if(texture_flags & R_TEXTURE_RGB)
return GL_RGB;
else if(texture_flags & R_TEXTURE_RGBA)
return GL_RGBA;
else if(texture_flags & R_TEXTURE_SRGB)
return GL_RGBA;
return GL_RGBA;
}
static GLenum gl_texture_type(u32 texture_flags)
{
if(texture_flags & R_TEXTURE_HDR)
return GL_FLOAT;
return GL_UNSIGNED_BYTE;
}

149
code/render/primitives.h Normal file
View File

@@ -0,0 +1,149 @@
#ifndef _PIUMA_RENDER_PRIMITIVES_H_
#define _PIUMA_RENDER_PRIMITIVES_H_
#include "../lib/types.h"
#include "../lib/math.h"
#include "GL/glcorearb.h"
enum r_texture_flags : u32
{
R_TEXTURE_NONE = 0x00,
R_TEXTURE_ALPHA = 0x01,
R_TEXTURE_RGB = 0x02,
R_TEXTURE_RGBA = 0x04,
R_TEXTURE_SRGB = 0x08,
R_TEXTURE_HDR = 0x10,
R_TEXTURE_NO_MIPMAP = 0x00010000,
R_TEXTURE_DONT_OWN = 0x20000000,
R_TEXTURE_INITIALIZED = 0x40000000,
R_TEXTURE_DESTROYED = 0x80000000
};
struct r_texture
{
u8 *data;
v2s size;
u32 flags;
// OpenGL
GLuint gl_id;
};
r_texture r_texture_create(u8 *data, v2s size, u32 flags);
void r_texture_destroy(r_texture *texture);
void r_texture_resize(r_texture *texture, v2s size);
void r_texture_update(r_texture *texture, u8 *data, v2s size, v2s position = {0,0}, u32 stride = 0);
s32 r_texture_channels(r_texture *texture);
enum r_cubemap_flags : u32
{
R_CUBEMAP_NONE = 0x00,
// R_TEXTURE_ALPHA = 0x01,
// R_TEXTURE_RGB = 0x02,
// R_TEXTURE_RGBA = 0x04,
// R_TEXTURE_SRGB = 0x08,
R_CUBEMAP_DONT_OWN = 0x20000000,
R_CUBEMAP_INITIALIZED = 0x40000000,
R_CUBEMAP_DESTROYED = 0x80000000
};
struct r_cubemap
{
float *data[6];
v2s size;
u32 flags;
// OpenGL
GLuint gl_id;
};
r_cubemap r_cubemap_create(float *data[6], v2s size, u32 flags);
void r_cubemap_destroy(r_cubemap *cubemap);
struct r_2d_mesh
{
v2 *vertices;
v4 *colors;
v2 *uvs;
u64 count;
// OpenGL
GLuint gl_VAO;
GLuint gl_vertex_buffer;
GLuint gl_color_buffer;
GLuint gl_uv_buffer;
};
r_2d_mesh r_2d_mesh_create(u64 count, v2 *vertices, v4 *colors, v2 *uvs = NULL);
void r_2d_mesh_destroy(r_2d_mesh *mesh);
// r_2d_mesh_update/change
struct r_mesh
{
v3 *vertices;
v3 *normals;
v3 *tangents;
v2 *uvs;
u64 vertices_count;
u32 *indices;
u64 indices_count;
// OpenGL
GLuint gl_VAO;
GLuint gl_vertex_buffer;
GLuint gl_normal_buffer;
GLuint gl_tangent_buffer;
GLuint gl_uv_buffer;
GLuint gl_index_buffer;
};
r_mesh r_mesh_create(u64 indices_count, u32 *indices, u64 vertices_count, v3 *vertices, v3 *normals = NULL, v3 *tangents = NULL, v2 *uvs = NULL);
void r_mesh_destroy(r_mesh *mesh);
// r_mesh_update/change
// Common meshes
r_mesh r_mesh_build_cube();
// Framebuffers
enum r_framebuffer_flags : u32
{
R_FRAMEBUFFER_NONE = 0,
R_FRAMEBUFFER_DEPTH,
};
struct r_framebuffer
{
u32 gl_id;
u32 gl_depth_id;
r_texture color_texture;
v2s size;
u32 flags;
};
r_framebuffer r_framebuffer_create(v2s size, u32 flags);
void r_framebuffer_destroy(r_framebuffer *fb);
void r_framebuffer_update_size(r_framebuffer *fb, v2s size);
#endif

513
code/render/render.cpp Normal file
View File

@@ -0,0 +1,513 @@
#include "render.h"
#include "state.h"
#include "../platform.h"
#include "../lib/math.h"
#include "../debug/logger.h"
#include "gl_helpers.h"
#include "string.h"
#include "../camera.h" // @Cleanup: remove dependency from camera
r_state r_render_state;
void r_init()
{
// OpenGL debug messages
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(glDebugOutput, nullptr);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
// Depth test
glEnable(GL_DEPTH_TEST);
// Face culling
glEnable(GL_CULL_FACE);
// Texture pixel alignment (default is 4, so rgb textures should have every pixel padded to 4 bytes. With this we can have 3 byte pixels with no padding between eachother)
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Blending
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
// Cubemap
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
// Load shaders
if(!r_shader_from_files(&r_render_state.shader_2d, "shaders/2d.vert", "shaders/2d.frag"))
{
LOG(LOG_ERROR, "Cannot load 2D shader.");
}
if(!r_shader_from_files(&r_render_state.shader_postprocessing, "shaders/postprocessing.vert", "shaders/postprocessing.frag"))
{
LOG(LOG_ERROR, "Cannot load postprocessing shader.");
}
if(!r_shader_from_files(&r_render_state.shader_pbr, "shaders/pbr.vert", "shaders/pbr.frag"))
{
LOG(LOG_ERROR, "Cannot load pbr shader.");
}
if(!r_shader_from_files(&r_render_state.shader_shadow_map, "shaders/shadow_map.vert", "shaders/shadow_map.frag"))
{
LOG(LOG_ERROR, "Cannot load shadow_map shader.");
}
if(!r_shader_from_files(&r_render_state.shader_environment_map, "shaders/environment_map.vert", "shaders/environment_map.frag"))
{
LOG(LOG_ERROR, "Cannot load environment_map shader.");
}
// Screen size
u32 width, height;
p_window_dimensions(&width, &height);
glViewport(0, 0, width, height);
/* We have to apply some post-processing effects only to the 3D world
* and some effects to both the 3D world and the HUD.
*
* So we make 2 framebuffers:
* 1. target for 3D world
* 2. target for HUD
* After rendering, we apply the correct post-processing effects and
* merge them toghether.
*/
// Init framebuffers
v2s size = {width, height};
r_render_state.framebuffer_SCREEN = {.gl_id = 0, .size = size, .flags = 0}; // This is a special framebuffer, because it's already there. We don't need to create it
r_render_state.framebuffer_HUD = r_framebuffer_create(size, 0);
r_render_state.framebuffer_3D = r_framebuffer_create(size, R_FRAMEBUFFER_DEPTH);
// Default framebuffer
r_framebuffer_select(&r_render_state.framebuffer_SCREEN);
// Init fullscreen quad
glGenVertexArrays(1, &r_render_state.gl_screen_quad_VAO);
glBindVertexArray(r_render_state.gl_screen_quad_VAO);
glGenBuffers(1, &r_render_state.gl_screen_quad_VBO);
glBindBuffer(GL_ARRAY_BUFFER, r_render_state.gl_screen_quad_VBO);
v2 quad_vertices[2*6] =
{
// position UV coords
v2{-1, 1}, v2{0, 1},
v2{-1, -1}, v2{0, 0},
v2{ 1, -1}, v2{1, 0},
v2{-1, 1}, v2{0, 1},
v2{ 1, -1}, v2{1, 0},
v2{ 1, 1}, v2{1, 1}
};
glBufferData(GL_ARRAY_BUFFER, 2*6 * sizeof(v2), quad_vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(v2), (void*)(0));
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2*sizeof(v2), (void*)(sizeof(v2)));
// Render state
r_size_update(width, height);
}
void r_deinit()
{
r_framebuffer_destroy(&r_render_state.framebuffer_HUD);
r_framebuffer_destroy(&r_render_state.framebuffer_3D);
glDeleteBuffers(1, &r_render_state.gl_screen_quad_VBO);
glDeleteVertexArrays(1, &r_render_state.gl_screen_quad_VAO);
}
void r_size_update(u32 width, u32 height)
{
// Screen size
r_render_state.width = width;
r_render_state.height = height;
// Update shaders
glUseProgram(r_render_state.shader_2d.id);
glUniform1i(r_render_state.shader_2d.width , width);
glUniform1i(r_render_state.shader_2d.height, height);
// Update framebuffers size
v2s size = {width, height};
r_render_state.framebuffer_SCREEN.size = size;
r_framebuffer_update_size(&r_render_state.framebuffer_HUD, size);
r_framebuffer_update_size(&r_render_state.framebuffer_3D , size);
}
void r_time_update(f64 time)
{
r_render_state.time = time;
}
void r_clear(v4 color)
{
glClearColor(color.r, color.g, color.b, color.a);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void r_swap()
{
p_graphics_swap();
}
r_scene r_scene_create()
{
r_scene scene;
scene.view_matrix = m4_identity;
scene.view_matrix_inverse = m4_identity;
scene.projection_matrix = m4_identity;
scene.projection_matrix_inverse = m4_identity;
scene.objects = NULL;
scene.objects_count = 0;
// Init lights memory
memset(&scene.lights, 0, sizeof(r_light_container));
glGenBuffers(1, &scene.gl_lights);
glBindBuffer(GL_UNIFORM_BUFFER, scene.gl_lights);
glBufferData(GL_UNIFORM_BUFFER, sizeof(r_light_container), &scene.lights, GL_STATIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, scene.gl_lights);
return scene;
}
void r_scene_destroy(r_scene *scene)
{
glDeleteBuffers(1, &scene->gl_lights);
r_cubemap_destroy(&scene->environment_map);
}
void r_scene_set_view(r_scene *scene, m4 m)
{
scene->view_matrix = m;
scene->view_matrix_inverse = inverse(m);
}
void r_scene_set_projection(r_scene *scene, m4 m)
{
scene->projection_matrix = m;
scene->projection_matrix_inverse = inverse(m);
}
void r_update_lights(r_scene *scene)
{
glBindBuffer(GL_UNIFORM_BUFFER, scene->gl_lights);
glBufferData(GL_UNIFORM_BUFFER, sizeof(r_light_container), &scene->lights, GL_STATIC_DRAW);
}
r_object build_basic_cube()
{
r_object object = r_object_allocate(1);
object.meshes[0] = (r_mesh*)p_alloc(sizeof(r_mesh));
*object.meshes[0] = r_mesh_build_cube();
object.mesh_material[0] = (r_material*)p_alloc(sizeof(r_material));
memset(object.mesh_material[0], 0, sizeof(r_material));
object.mesh_material[0]->shader = &r_render_state.shader_pbr;
object.mesh_material[0]->albedo_factor = {1,1,1,1};
object.mesh_material[0]->emissive_factor = {1,1,1,0};
return object;
}
void r_render_scene(r_scene *scene)
{
// Render shadow maps
scene->shadow_map = r_build_shadow_map_sun(scene, &scene->lights.sun_lights[0]);
// Render environment map
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
static r_object cube = build_basic_cube();
cube.position = extract_column(scene->view_matrix_inverse, 3).xyz;
glUseProgram(r_render_state.shader_environment_map.id);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, scene->environment_map.gl_id);
glUniform1i(r_render_state.shader_environment_map.has_environment_map, 1);
glUniform1i(r_render_state.shader_environment_map.environment_map , 0);
r_render_object(scene, &cube, &r_render_state.shader_environment_map);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
// Render objects
glUseProgram(r_render_state.shader_pbr.id);
// Set shadow map
// @Cleanup: this should be in r_render_objects
glActiveTexture(GL_TEXTURE5);
glBindTexture(GL_TEXTURE_2D, scene->shadow_map.gl_depth_texture);
glUniform1i (r_render_state.shader_pbr.has_shadow_map, 1);
glUniform1i (r_render_state.shader_pbr.shadow_map , 5);
glUniformMatrix4fv(r_render_state.shader_pbr.shadow_matrix , 1, GL_TRUE, (const f32 *)(scene->shadow_map.view_matrix.E));
// Environment map
glActiveTexture(GL_TEXTURE6);
glBindTexture(GL_TEXTURE_CUBE_MAP, scene->environment_map.gl_id);
glUniform1i(r_render_state.shader_pbr.has_environment_map, 1);
glUniform1i(r_render_state.shader_pbr.environment_map , 6);
// Render objects
for(u32 i = 0; i < scene->objects_count; i++)
{
r_render_object(scene, &scene->objects[i]);
}
glUniform1i(r_render_state.shader_pbr.has_shadow_map, 0);
glUniform1i(r_render_state.shader_pbr.has_environment_map, 0);
r_free_shadow_map(&scene->shadow_map);
}
// Single object rendering functions (batched funcs are below)
void r_render_object(r_scene *scene, r_object *object, r_shader *shader_override)
{
// @Performance: Minimize draw calls by batching mesh rendering
for(u32 i = 0; i < object->count; i++)
{
r_mesh *mesh = object->meshes[i];
r_material *material = object->mesh_material[i];
r_shader *shader = material->shader;
if(shader_override)
shader = shader_override;
glUseProgram(shader->id);
glUniform1f(shader->time , (f32)r_render_state.time );
glUniform1f(shader->width , (f32)r_render_state.width );
glUniform1f(shader->height, (f32)r_render_state.height);
// Transform matrices
m4 projview_matrix = scene->projection_matrix * scene->view_matrix;
m4 projview_matrix_inverse = inverse(projview_matrix);
glUniformMatrix4fv(shader->view_matrix, 1, GL_TRUE, (const f32 *)(&projview_matrix));
glUniformMatrix4fv(shader->view_matrix_inverse, 1, GL_TRUE, (const f32 *)(&projview_matrix_inverse));
m4 model_transform = r_object_mesh_transform_matrix(object, i);
glUniformMatrix4fv(shader->model_matrix, 1, GL_TRUE, (const f32 *)(&model_transform));
// Textures
// Albedo
glUniform1i(shader->has_albedo_texture, material->albedo_texture != NULL);
if(material->albedo_texture)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, material->albedo_texture->gl_id);
glUniform1i(shader->albedo_texture, 0);
}
glUniform4f(shader->albedo_factor, material->albedo_factor.r, material->albedo_factor.g, material->albedo_factor.b, material->albedo_factor.a);
// Metallic
glUniform1i(shader->has_metallic_texture, material->metallic_texture != NULL);
if(material->metallic_texture)
{
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, material->metallic_texture->gl_id);
glUniform1i(shader->metallic_texture, 1);
}
glUniform1f(shader->metallic_factor, material->metallic_factor);
// Roughness
glUniform1i(shader->has_roughness_texture, material->roughness_texture != NULL);
if(material->roughness_texture)
{
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, material->roughness_texture->gl_id);
glUniform1i(shader->roughness_texture, 2);
}
glUniform1f(shader->roughness_factor, material->roughness_factor);
// Normal
glUniform1i(shader->has_normal_texture, material->normal_texture != NULL);
if(material->normal_texture)
{
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, material->normal_texture->gl_id);
glUniform1i(shader->normal_texture, 3);
}
// Emissive
glUniform1i(shader->has_emissive_texture, material->emissive_texture != NULL);
if(material->emissive_texture)
{
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, material->emissive_texture->gl_id);
glUniform1i(shader->emissive_texture, 4);
}
glUniform4f(shader->emissive_factor, material->emissive_factor.r, material->emissive_factor.g, material->emissive_factor.b, material->emissive_factor.a);
// Draw call
glBindVertexArray(mesh->gl_VAO);
glDrawElements(GL_TRIANGLES, mesh->indices_count, GL_UNSIGNED_INT, (void*)(0));
}
}
void r_render_object_wireframe(r_scene *scene, r_object *object)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
r_render_object(scene, object);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
void r_render_line(r_scene *scene, v3 a, v3 b, v4 a_color, v4 b_color, f32 thickness)
{
m4 projview_matrix = scene->projection_matrix * scene->view_matrix;
v4 a_trans = projview_matrix * V4(a, 1);
v4 b_trans = projview_matrix * V4(b, 1);
a_trans /= a_trans.w;
b_trans /= b_trans.w;
if(a_trans.z > 1 || b_trans.z > 1)
return;
v2 a_2d = a_trans.xy;
v2 b_2d = b_trans.xy;
a_2d = (v2{.5,.5} + v2{.5,-0.5} * a_2d) * v2{(f32)r_render_state.width, (f32)r_render_state.height};
b_2d = (v2{.5,.5} + v2{.5,-0.5} * b_2d) * v2{(f32)r_render_state.width, (f32)r_render_state.height};
r_2d_immediate_segment(a_2d, b_2d, a_color, b_color, thickness); // @Cleanup: This should be independent from the 2D drawing functions, probably.
}
// Batch render functions
void r_render_objects(r_scene *scene, u64 count, r_object **objects)
{
// @Feature: implement
}
r_shadow_map r_build_shadow_map_sun(r_scene *scene, r_sun_light *sun)
{
glCullFace(GL_FRONT);
r_shadow_map sm;
u32 width = 2*1024;
u32 height = 2*1024;
// Init depth texture/framebuffer
glGenTextures(1, &sm.gl_depth_texture);
glBindTexture(GL_TEXTURE_2D, sm.gl_depth_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
glGenFramebuffers(1, &sm.gl_FBO);
glBindFramebuffer(GL_FRAMEBUFFER, sm.gl_FBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, sm.gl_depth_texture, 0);
glDrawBuffer(GL_NONE);
glBindTexture(GL_TEXTURE_2D, 0);
// Compute light frustum
m4 projview_matrix = scene->projection_matrix * scene->view_matrix;
m4 projview_matrix_inverse = inverse(projview_matrix);
v4 center_point_4 = projview_matrix_inverse * v4{0, 0, 0, 1};
v3 center_point = center_point_4.xyz / center_point_4.w;
f32 size = 1;
v4 bottom = projview_matrix_inverse * v4{ 0,-1, 0, 1};
v4 top = projview_matrix_inverse * v4{ 0, 1, 0, 1};
v4 left = projview_matrix_inverse * v4{-1, 0, 0, 1};
v4 right = projview_matrix_inverse * v4{ 1, 0, 0, 1};
v4 front = projview_matrix_inverse * v4{ 0, 0,-1, 1};
v4 back = projview_matrix_inverse * v4{ 0, 0, 1, 1};
size = maximum(size, length(top.xyz/top.w - bottom.xyz/bottom.w));
size = maximum(size, length(right.xyz/right.w - left.xyz/left.w));
size = maximum(size, length(back.xyz/back.w - front.xyz/front.w));
size /= 2; // @Cleanup: hack to have better precision. There are multiple ways to do it right: smallest box around all objects, cascaded shadow maps
v3 light_position = center_point - size * sun->direction;
m4 light_camera_matrix = r_view_matrix(light_position, sun->direction, v3{0, 0, 1});
m4 projection_matrix = r_orthographic_matrix(-size/2, size/2, -size/2, size/2, 0.1, 100.0);
sm.view_matrix = projection_matrix * light_camera_matrix;
// Render shadow map
glViewport(0, 0, width, height);
glClear(GL_DEPTH_BUFFER_BIT);
// @Feature @Cleanup: Some objects might not need to be drawn. Add a "hidden" flag. Maybe take the list of objects to render as an argument
m4 old_view = scene->view_matrix;
m4 old_proj = scene->projection_matrix;
r_scene_set_view (scene, sm.view_matrix);
r_scene_set_projection(scene, m4_identity);
for(u32 i = 0; i < scene->objects_count; i++)
{
if(scene->objects[i].has_shadow)
{
r_render_object(scene, &scene->objects[i], &r_render_state.shader_shadow_map);
}
}
r_scene_set_view (scene, old_view);
r_scene_set_projection(scene, old_proj);
// Restore framebuffer
r_framebuffer_select(r_render_state.current_framebuffer);
glCullFace(GL_BACK);
return sm;
}
void r_free_shadow_map(r_shadow_map *sm)
{
glDeleteTextures(1, &sm->gl_depth_texture);
glDeleteFramebuffers(1, &sm->gl_FBO);
}
void r_merge_and_postprocess()
{
glDisable(GL_DEPTH_TEST);
glEnable(GL_FRAMEBUFFER_SRGB);
glUseProgram(r_render_state.shader_postprocessing.id);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, r_render_state.framebuffer_3D.color_texture.gl_id);
glUniform1i(r_render_state.shader_postprocessing.texture[0], 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, r_render_state.framebuffer_HUD.color_texture.gl_id);
glUniform1i(r_render_state.shader_postprocessing.texture[1], 1);
glBindVertexArray(r_render_state.gl_screen_quad_VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glEnable(GL_DEPTH_TEST);
glDisable(GL_FRAMEBUFFER_SRGB);
}
void r_framebuffer_select(r_framebuffer *fb)
{
glBindFramebuffer(GL_FRAMEBUFFER, fb->gl_id);
glViewport(0, 0, fb->size.x, fb->size.y);
glUseProgram(r_render_state.shader_2d.id);
glUniform1i(r_render_state.shader_2d.width , fb->size.x);
glUniform1i(r_render_state.shader_2d.height, fb->size.y);
r_render_state.current_framebuffer = fb;
}

90
code/render/render.h Normal file
View File

@@ -0,0 +1,90 @@
// 2d: Functions to render 2d things immediatly. Simple things like meshes, lines, triangles, quads.
// render: Renderer, takes complex objects with materials and renders them.
// Might use an object buffer/queue and render things later, to enable
// sorting and other complex rendering procedures (culling).
// shader: Shaders! They have enough code to have their own source files
// object: Objects, materials, maybe other things. To be used by the renderer.
// light: Lights to be used by the renderer. There are enought types to separate them from objects.
// Might (or might not) add: compute shaders, vfx/postprocessing passes, animations
#ifndef _PIUMA_RENDER_RENDER_H_
#define _PIUMA_RENDER_RENDER_H_
#include "primitives.h"
#include "shader.h"
#include "2d.h"
#include "lights.h"
#include "object.h"
#include "state.h"
void r_init();
void r_deinit();
void r_size_update(u32 width, u32 height);
void r_time_update(f64 time);
void r_clear(v4 color = {0.0, 0.0, 0.0, 0.0});
void r_swap();
struct r_shadow_map
{
// FBO + depth
u32 gl_FBO;
u32 gl_depth_texture;
m4 view_matrix;
};
struct r_scene
{
m4 view_matrix;
m4 view_matrix_inverse;
m4 projection_matrix;
m4 projection_matrix_inverse;
r_object *objects;
u64 objects_count;
r_light_container lights;
GLuint gl_lights;
r_shadow_map shadow_map;
r_cubemap environment_map;
};
r_scene r_scene_create();
void r_scene_destroy(r_scene *scene);
void r_scene_set_view (r_scene *scene, m4 m);
void r_scene_set_projection(r_scene *scene, m4 m);
void r_update_lights(r_scene *scene);
void r_render_scene(r_scene *scene);
// Single object rendering functions (batched funcs are below)
void r_render_object(r_scene *scene, r_object *object, r_shader *shader_override = NULL);
void r_render_object_wireframe(r_scene *scene, r_object *object);
void r_render_line(r_scene *scene, v3 a, v3 b, v4 a_color, v4 b_color, f32 thickness = 1.0);
// Batch render functions
void r_render_objects(r_scene *scene, u64 count, r_object **objects);
// Shadow maps
r_shadow_map r_build_shadow_map_sun(r_scene *scene, r_sun_light *sun);
void r_free_shadow_map(r_shadow_map *sm);
void r_framebuffer_select(r_framebuffer *fb);
void r_merge_and_postprocess();
#endif

193
code/render/shader.cpp Normal file
View File

@@ -0,0 +1,193 @@
#include "shader.h"
#include "../debug/logger.h"
#define STB_INCLUDE_IMPLEMENTATION
#define STB_INCLUDE_LINE_GLSL
#include "stb_include.h"
// Internal functions
void r_shader_set_uniform_locations(r_shader *shader);
bool r_shader_from_files(r_shader *shader, const char *vertex, const char *fragment, const char *geometry, const char *tesseletion_control, const char *tesseletion_evaluation, const char *compute)
{
const int count = 6;
char *source[count] = {NULL, NULL, NULL, NULL, NULL, NULL};
const char *filename[count] = {vertex, fragment, geometry, tesseletion_control, tesseletion_evaluation, compute};
const char *type[count] = {"vertex", "fragment", "geometry", "tesseletion_control", "tesseletion_evaluation", "compute"};
// Load sources
bool has_error = false;
char error[256];
for(int i = 0; i < count; i++)
{
if(filename[i])
{
char path[4096];
u32 len = strlen(filename[i]);
memcpy(path, filename[i], len+1);
int last_slash = len;
while(last_slash > 0)
{
if(filename[i][last_slash] == '/')
break;
last_slash--;
}
path[last_slash] = '\0';
source[i] = stb_include_file((char*)filename[i], NULL, path, error);
if(!source[i])
{
has_error = true;
LOG(LOG_ERROR, "Error loading %s shader: %s", type[i], error);
break;
}
}
}
// Compile shader
if(!has_error)
{
has_error = !r_shader_from_text(shader, source[0], source[1], source[2], source[3], source[5], source[5]);
}
// Cleanup
for(int i = 0; i < count; i++)
{
if(source[i])
free(source[i]);
}
return !has_error;
}
bool r_shader_from_text(r_shader *shader, const char *vertex, const char *fragment, const char *geometry, const char *tesseletion_control, const char *tesseletion_evaluation, const char *compute)
{
int count = 6;
GLuint type[count] = {GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, GL_TESS_CONTROL_SHADER, GL_TESS_EVALUATION_SHADER, GL_COMPUTE_SHADER};
const char *source[count] = {vertex, fragment, geometry, tesseletion_control, tesseletion_evaluation, compute};
// Create shader program
shader->id = glCreateProgram();
bool has_error = false;
for(int i = 0; i < count; i++)
{
if(!source[i])
continue;
// Compile current shader source
GLuint source_id = glCreateShader(type[i]);
GLint size = strlen(source[i]);
glShaderSource(source_id, 1, &source[i], &size);
glCompileShader(source_id);
// Check for success/errors
char build_info[2048];
GLint build_success;
glGetShaderiv(source_id, GL_COMPILE_STATUS, &build_success);
if (!build_success)
{
glGetShaderInfoLog(source_id, 2048, NULL, build_info);
LOG(LOG_ERROR, "Cannot compile shader %.*s: %s", (int)size, source, build_info);
glDeleteShader(source_id);
has_error = true;
break;
}
// Attach compiler shader to program
glAttachShader(shader->id, source_id);
}
// @Correctness: Clean up shader sources after fail
if(has_error)
return false;
// Link program
glLinkProgram(shader->id);
// Check for success/error
GLint build_success;
char build_info[512];
glGetProgramiv(shader->id, GL_LINK_STATUS, &build_success);
if(!build_success) {
glGetProgramInfoLog(shader->id, 512, NULL, build_info);
LOG(LOG_ERROR, "Cannot link shader program: %s", build_info);
glDeleteShader(shader->id);
has_error = true;
}
// Load uniform locations
if(!has_error)
r_shader_set_uniform_locations(shader);
return !has_error;
}
void r_shader_delete(r_shader *shader)
{
glDeleteProgram(shader->id);
}
// Internal functions
void r_shader_set_uniform_locations(r_shader *shader)
{
shader->time = glGetUniformLocation(shader->id, "time");
shader->width = glGetUniformLocation(shader->id, "width");
shader->height = glGetUniformLocation(shader->id, "height");
shader->view_matrix = glGetUniformLocation(shader->id, "view_matrix");
shader->view_matrix_inverse = glGetUniformLocation(shader->id, "view_matrix_inverse");
shader->model_matrix = glGetUniformLocation(shader->id, "model_matrix");
shader->camera_position = glGetUniformLocation(shader->id, "camera_position");
for(int i = 0; i < R_SHADER_COLOR_MAX; i++)
{
char uniform_name[32]; sprintf(uniform_name, "color%d", i);
shader->color[i] = glGetUniformLocation(shader->id, uniform_name);
}
for(int i = 0; i < R_SHADER_TEXTURES_MAX; i++)
{
char uniform_name[32];
sprintf(uniform_name, "has_texture%d", i);
shader->has_texture [i] = glGetUniformLocation(shader->id, uniform_name);
sprintf(uniform_name, "texture%d", i);
shader->texture [i] = glGetUniformLocation(shader->id, uniform_name);
sprintf(uniform_name, "texture_channels%d", i);
shader->texture_channels[i] = glGetUniformLocation(shader->id, uniform_name);
}
shader->lights = glGetUniformBlockIndex(shader->id, "lights");
if(shader->lights != GL_INVALID_INDEX)
glUniformBlockBinding(shader->id, shader->lights, 0);
shader->has_shadow_map = glGetUniformLocation(shader->id, "has_shadow_map");
shader->shadow_map = glGetUniformLocation(shader->id, "shadow_map");
shader->shadow_matrix = glGetUniformLocation(shader->id, "shadow_matrix");
shader->has_environment_map = glGetUniformLocation(shader->id, "has_environment_map");
shader->environment_map = glGetUniformLocation(shader->id, "environment_map");
shader->has_albedo_texture = glGetUniformLocation(shader->id, "has_albedo_texture");
shader->albedo_texture = glGetUniformLocation(shader->id, "albedo_texture");
shader->albedo_factor = glGetUniformLocation(shader->id, "albedo_factor");
shader->has_metallic_texture = glGetUniformLocation(shader->id, "has_metallic_texture");
shader->metallic_texture = glGetUniformLocation(shader->id, "metallic_texture");
shader->metallic_factor = glGetUniformLocation(shader->id, "metallic_factor");
shader->has_roughness_texture = glGetUniformLocation(shader->id, "has_roughness_texture");
shader->roughness_texture = glGetUniformLocation(shader->id, "roughness_texture");
shader->roughness_factor = glGetUniformLocation(shader->id, "roughness_factor");
shader->has_normal_texture = glGetUniformLocation(shader->id, "has_normal_texture");
shader->normal_texture = glGetUniformLocation(shader->id, "normal_texture");
shader->has_emissive_texture = glGetUniformLocation(shader->id, "has_emissive_texture");
shader->emissive_texture = glGetUniformLocation(shader->id, "emissive_texture");
shader->emissive_factor = glGetUniformLocation(shader->id, "emissive_factor");
}

85
code/render/shader.h Normal file
View File

@@ -0,0 +1,85 @@
#ifndef _PIUMA_RENDER_SHADER_H_
#define _PIUMA_RENDER_SHADER_H_
#include "../lib/types.h"
#include "GL/glcorearb.h"
/*
I put the uniform ids of all the shaders in the r_shader structure. Then I use only the
ones I need. Yeah, it's not very flexible.
I would like to have automatic syncronization between shader and C++ code.
There are 3 ways to do that:
- Parse shader code and auto-generate C++ structures and code;
- Parse C++ structures and generate a shader source, or update an existing one;
- Use a language than let me define shader code directly in that language, then it's compiled
directly to GPU ASM, or it generates GLSL code that is then given to the GPU driver. This is
the ideal solution, but yeah, that language is not C++... if it event exists. Let's hope for
a better future.
*/
#define R_SHADER_TEXTURES_MAX 4
#define R_SHADER_COLOR_MAX 4
struct r_shader
{
GLuint id;
// Common state
GLint time;
GLint width;
GLint height;
// View and camera
GLint view_matrix;
GLint view_matrix_inverse;
GLint model_matrix;
GLint camera_position;
// For arrays, the name in the shader is in the format [name][index].
// Example: color[4] will be: color0, color1, color2, color3
// Generic parameters that can be used for different purpose based on the shader needs.
// Example: texture1 could be a diffuse texture, texture2 an emissive texture, texture3 bump mapping
GLint color[R_SHADER_COLOR_MAX];
GLint has_texture [R_SHADER_TEXTURES_MAX]; // Is textureX assigned or not?
GLint texture [R_SHADER_TEXTURES_MAX]; // Actual texture
GLint texture_channels[R_SHADER_TEXTURES_MAX]; // Number of channels in the texture data
// Lights and shadows
// @Cleanup: maybe merge this with the generic texture parameter?
GLint lights;
GLint has_shadow_map;
GLint shadow_map;
GLint shadow_matrix;
// Environment map
GLint has_environment_map;
GLint environment_map;
// PBR material
GLint has_albedo_texture;
GLint albedo_texture;
GLint albedo_factor;
GLint has_metallic_texture;
GLint metallic_texture;
GLint metallic_factor;
GLint has_roughness_texture;
GLint roughness_texture;
GLint roughness_factor;
GLint has_normal_texture;
GLint normal_texture;
GLint has_emissive_texture;
GLint emissive_texture;
GLint emissive_factor;
};
bool r_shader_from_files(r_shader *shader, const char *vertex, const char *fragment, const char *geometry = NULL, const char *tesseletion_control = NULL, const char *tesseletion_evaluation = NULL, const char *compute = NULL);
bool r_shader_from_text(r_shader *shader, const char *vertex, const char *fragment, const char *geometry = NULL, const char *tesseletion_control = NULL, const char *tesseletion_evaluation = NULL, const char *compute = NULL);
void r_shader_delete(r_shader *shader);
#endif

36
code/render/state.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef _PIUMA_RENDER_STATE_H_
#define _PIUMA_RENDER_STATE_H_
#include "shader.h"
#include "primitives.h"
struct r_state
{
// Shaders
r_shader shader_2d;
r_shader shader_postprocessing;
r_shader shader_pbr;
r_shader shader_shadow_map;
r_shader shader_environment_map;
// Screen size
u32 width, height;
// Time
f64 time;
// Framebuffers
r_framebuffer *current_framebuffer;
r_framebuffer framebuffer_SCREEN;
r_framebuffer framebuffer_HUD;
r_framebuffer framebuffer_3D;
// Quads
u32 gl_screen_quad_VAO;
u32 gl_screen_quad_VBO;
};
extern r_state r_render_state;
#endif