Merge pull request #2 from SeanOMik/feature/texture-maps

Feature/texture maps
This commit is contained in:
SeanOMik 2022-10-11 23:21:29 -04:00 committed by GitHub
commit 62321132cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 684 additions and 414 deletions

39
.clang-tidy Normal file
View File

@ -0,0 +1,39 @@
---
Checks: "clang-diagnostic-*,clang-analyzer-*,cppcoreguidelines-*,modernize-*,-modernize-use-trailing-return-type"
WarningsAsErrors: true
HeaderFilterRegex: ""
AnalyzeTemporaryDtors: false
FormatStyle: google
CheckOptions:
- key: cert-dcl16-c.NewSuffixes
value: "L;LL;LU;LLU"
- key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField
value: "0"
- key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors
value: "1"
- key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
value: "1"
- key: google-readability-braces-around-statements.ShortStatementLines
value: "1"
- key: google-readability-function-size.StatementThreshold
value: "800"
- key: google-readability-namespace-comments.ShortNamespaceLines
value: "10"
- key: google-readability-namespace-comments.SpacesBeforeComments
value: "2"
- key: modernize-loop-convert.MaxCopySize
value: "16"
- key: modernize-loop-convert.MinConfidence
value: reasonable
- key: modernize-loop-convert.NamingStyle
value: CamelCase
- key: modernize-pass-by-value.IncludeStyle
value: llvm
- key: modernize-replace-auto-ptr.IncludeStyle
value: llvm
- key: modernize-use-nullptr.NullMacros
value: "NULL"
- key: readability-magic-numbers
value: "0"
---

13
CMake/Getassimp.cmake Normal file
View File

@ -0,0 +1,13 @@
include(ExternalProject)
ExternalProject_Add(external_assimp
GIT_REPOSITORY https://github.com/assimp/assimp.git
GIT_TAG v5.2.5
GIT_SUBMODULES_RECURSE true
GIT_PROGRESS true
CMAKE_ARGS
-D CMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}
-D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-D ASSIMP_INSTALL=NO
-D ASSIMP_BUILD_TESTS=NO
)
message("Downloaded assimp library")

View File

@ -21,7 +21,7 @@ find_package(GLEW REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
find_package(glm CONFIG REQUIRED)
find_package(OpenGL REQUIRED)
find_package(assimp REQUIRED)
find_package(assimp CONFIG REQUIRED)
# Link sources
file(GLOB_RECURSE source_list src/*.cpp)
@ -45,7 +45,14 @@ target_link_libraries(simpleengine PUBLIC GLEW::GLEW)
target_link_libraries(simpleengine PUBLIC glfw)
target_link_libraries(simpleengine PUBLIC ${GLM_LIBRARIES})
target_link_libraries(simpleengine PUBLIC ${OPENGL_LIBRARIES})
target_link_libraries(simpleengine PUBLIC assimp)
if(WIN32)
target_link_libraries(simpleengine PUBLIC assimp::assimp)
else()
target_link_libraries(simpleengine PUBLIC assimp)
endif()
# Link resources
target_link_libraries(simpleengine PUBLIC cmrc::base)
target_link_libraries(simpleengine PRIVATE simpleengine_resources)
# Include some dependencies' include directories

View File

@ -9,17 +9,17 @@ target_sources(dev_testing PRIVATE ${source_list})
target_include_directories(dev_testing PUBLIC include)
# Embed shaders
file(GLOB_RECURSE shaders_list resources/shaders/*.glsl)
cmrc_add_resource_library(
resource_shaders
WHENCE resources/shaders
PREFIX shaders
${shaders_list}
)
#file(GLOB_RECURSE shaders_list resources/shaders/*.glsl)
#cmrc_add_resource_library(
# resource_shaders
# WHENCE resources/shaders
# PREFIX shaders
# ${shaders_list}
#)
# Link simpleengine
target_link_libraries(dev_testing PUBLIC simpleengine)
target_link_libraries(dev_testing PRIVATE resource_shaders)
#target_link_libraries(dev_testing PRIVATE resource_shaders)
# Set standard to C++20
set_target_properties(dev_testing PROPERTIES CXX_STANDARD 20 CXX_EXTENSIONS OFF)

View File

@ -1,46 +0,0 @@
#version 440
in vec3 vs_position;
in mat4 vs_transform;
in vec2 vs_texcoord;
in vec3 vs_normal;
in vec3 vs_to_light;
in vec3 vs_to_camera;
uniform bool texture_is_set;
uniform sampler2D vs_texture;
uniform vec3 light_color;
uniform float shine_damper;
uniform float reflectivity;
out vec4 fs_color;
void main() {
// Lighting
vec3 unit_normal = normalize(vs_normal);
vec3 unit_light_vector = normalize(vs_to_light);
float dot_prod = dot(unit_normal, unit_light_vector);
float brightness = max(dot_prod, 0.f);
vec3 diffuse = brightness * light_color;
// Specular lighting
// only do all this math is reflectivity is > 0
vec3 final_specular = vec3(0.f);
if (reflectivity > 0) {
vec3 unit_vector_to_camera = normalize(vs_to_camera);
vec3 light_direction = -unit_vector_to_camera;
vec3 reflected_light_dir = reflect(light_direction, unit_normal);
float specular_factor = dot(reflected_light_dir, unit_vector_to_camera);
specular_factor = max(specular_factor, 0.f);
float damped_specular = pow(specular_factor, shine_damper);
final_specular = damped_specular * reflectivity * light_color;
}
if (texture_is_set) {
//fs_color = vec4(0.5 * unit_normal + vec3(0.5), 1.f); // Visualize normals
fs_color = vec4(diffuse, 1.f) * texture(vs_texture, vs_texcoord) + vec4(final_specular, 1.f);
} else {
fs_color = vec4(diffuse, 1.f) + vec4(final_specular, 1.f);
}
}

View File

@ -1,31 +0,0 @@
#version 440
layout (location = 0) in vec3 vertex_position;
layout (location = 1) in vec2 vertex_texcoord;
layout (location = 2) in vec3 vertex_normal;
out vec3 vs_position;
out vec2 vs_texcoord;
out mat4 vs_transform;
out vec3 vs_normal;
out vec3 vs_to_light;
out vec3 vs_to_camera;
uniform mat4 transform_matrix;
uniform mat4 view_matrix;
uniform mat4 projection_matrix;
uniform vec3 light_position;
void main() {
vec4 world_pos = (transform_matrix * vec4(vertex_position, 1.f));
vs_position = world_pos.xyz;
vs_transform = transform_matrix;
vs_texcoord = vertex_texcoord;
gl_Position = projection_matrix * view_matrix * world_pos;
vs_normal = (transform_matrix * vec4(vertex_normal, 0.f)).xyz;
vs_to_light = light_position - world_pos.xyz;
vs_to_camera = (inverse(view_matrix) * vec4(0.f, 0.f, 0.f, 1.f)).xyz - world_pos.xyz;
}

View File

@ -1,8 +1,6 @@
#include "simpleengine/camera.h"
#include "simpleengine/ecs/component/mesh_component.h"
#include <simpleengine/ecs/component/model_component.h>
#include "simpleengine/ecs/component/transform_component.h"
#include <simpleengine/ecs/component/rotating_component.h>
#include "simpleengine/ecs/entity.h"
#include "simpleengine/gfx/light.h"
#include "simpleengine/gfx/material.h"
@ -11,29 +9,28 @@
#include "simpleengine/gfx/renderer.h"
#include "simpleengine/gfx/texture.h"
#include "simpleengine/vector.h"
#include <simpleengine/gfx/shader.h>
#include <simpleengine/renderable.h>
#include <simpleengine/ecs/component/model_component.h>
#include <simpleengine/ecs/component/rotating_component.h>
#include <simpleengine/event/event.h>
#include <simpleengine/shader_program.h>
#include <simpleengine/game.h>
#include <simpleengine/vertex.h>
#include <simpleengine/gfx/shaders/core_3d_shader.h>
#include <simpleengine/gfx/model.h>
#include <simpleengine/gfx/shader.h>
#include <simpleengine/gfx/shaders/core_3d_shader.h>
#include <simpleengine/renderable.h>
#include <simpleengine/scene.h>
#include <simpleengine/shader_program.h>
#include <simpleengine/vertex.h>
#include <assimp/material.h>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/fwd.hpp>
#include <assimp/material.h>
#include <memory>
#include <chrono>
#include <iostream>
#include <memory>
#include <sstream>
#include <stdint.h>
#include <cmrc/cmrc.hpp>
CMRC_DECLARE(resource_shaders);
namespace se = simpleengine;
class FPSCounterEvent : public se::Event {
@ -46,13 +43,13 @@ public:
frame_count = 0;
}
virtual void update(const float& delta_time) {
virtual void update(const float &delta_time) {
double current_time = glfwGetTime();
frame_count++;
// Check if the last print was 1 second ago.
if (current_time - last_frame_time >= 1.0) {
double ms_per_frame = 1000 / (double) frame_count;
double ms_per_frame = 1000 / (double)frame_count;
printf("%d fps, %f ms/frame\n", frame_count, ms_per_frame);
frame_count = 0;
@ -61,158 +58,52 @@ public:
}
};
std::string read_resource_shader(const std::string& path) {
auto fs = cmrc::resource_shaders::get_filesystem();
cmrc::file vertex_file = fs.open(path);
return std::string(vertex_file.begin());
}
int main(int argc, char *argv[]) {
se::Game game(640, 480, "SimpleEngine 3D OpenGL - Developer Testing", GLFW_OPENGL_CORE_PROFILE, 4, 4, false);
/* se::gfx::Texture wall_texture("resources/wall.jpg");
se::gfx::Texture crate_texture("resources/container.jpg", true, true); */
// Load core shaders from SimpleEngine resources
se::gfx::shaders::Core3dShader core_shader;
auto light = std::make_shared<se::gfx::Light>(core_shader, glm::vec3(0.f, 1.f, -10.f), glm::vec3(1.f, 1.f, 1.f));
game.add_event(light);
auto white_texture = se::gfx::Texture::white_texture();
// white_texture.shine_damper = 10;
//white_texture.reflectivity = 1;
/* auto dragon = std::make_shared<se::objects_3d::Mesh>(game.get_window(), core_shader, white_texture, "examples/dev_testing/resources/dragon.obj");
dragon->translate(0.f, -5.f, -15.f);
game.add_event(dragon); */
/* auto cube = std::make_shared<se::objects_3d::Mesh>(game.get_window(), core_shader, white_texture, "examples/dev_testing/resources/cube.obj");
cube->translate(0.f, -5.f, -15.f);
game.add_event(cube);
se::gfx::Texture grass("examples/dev_testing/resources/grass.png");
auto terrain = std::make_shared<se::objects_3d::Terrain>(game.get_window(), core_shader, grass, 0, 0);
terrain->translate(0.f, -5.f, -15.f);
game.add_event(terrain); */
/* se::gfx::Texture stall_texture("examples/dev_testing/resources/stallTextureb.png");
auto stall = std::make_shared<se::objects_3d::Mesh>(game.get_window(), core_shader, stall_texture, "examples/dev_testing/resources/stall.obj");
stall->translate(10.f, -5.f, 0.f);
stall->rotate_y(90.f);
game.add_event(stall); */
/* std::vector<se::Vertex> square_vertices = {
{ se::Vectorf(0.5f, 0.5f, -1.f), glm::vec3(1.f, 0.f, 0.f), glm::vec2(0.f, 0.f) }, // top right
{ se::Vectorf(0.5f, -0.5f, -1.f), glm::vec3(0.f, 1.f, 0.f), glm::vec2(1.f, 0.f) }, // bottom right
{ se::Vectorf(-0.5f, -0.5f, -1.f), glm::vec3(0.f, 0.f, 1.f), glm::vec2(0.5f, 1.0f) }, // bottom left
{ se::Vectorf(-0.5f, 0.5f, -1.f), glm::vec3(.5f, 0.5f, 0.f), glm::vec2(0.5f, 1.0f) }, // top left
};
std::vector<GLuint> indicies = {
0, 1, 3,
1, 2, 3
};
auto square = std::make_shared<se::gfx::Model>(game.get_window(), core_shader, square_vertices, indicies);
square->translate(1.25f, 0.f, -1.f);
//square->rotate_y(90.f);
//square->scale(.75f);
game.add_event(square); */
/* se::gfx::Texture white_texture("examples/dev_testing/resources/white_texture.png");
auto cube = std::make_shared<se::objects_3d::Mesh>(game.get_window(), core_shader, white_texture, "examples/dev_testing/resources/cube.obj");
cube->translate(-1.25f, 0.f, -1.f);
game.add_event(cube); */
/* std::vector<se::Vertex> tri_vertices = {
{ se::Vectorf(-0.5f, -0.5f, -1.f), glm::vec3(1.f, 0.f, 0.f), glm::vec2(0.f, 0.f) }, // top right
{ se::Vectorf(0.5f, -0.5f, -1.f), glm::vec3(0.f, 1.f, 0.f), glm::vec2(1.f, 0.f) }, // bottom right
{ se::Vectorf(0.f, 0.5f, -1.f), glm::vec3(0.f, 0.f, 1.f), glm::vec2(0.5f, 1.0f) }, // bottom left
};
auto tri = std::make_shared<se::gfx::Model>(game.get_window(), core_shader, tri_vertices);
tri->translate(-1.25f, 0.f, -1.f);
tri->scale(.75f);
game.add_event(tri); */
std::vector<se::LitVertex> cube_vertices = {
{ se::Vectorf(-1.f, -1.f, -1.f), glm::vec3(1.f, 0.f, 0.f), glm::vec2(0.f, 0.f) },
{ se::Vectorf(1.f, -1.f, -1.f), glm::vec3(0.f, 1.f, 0.f), glm::vec2(1.f, 0.f) },
{ se::Vectorf(1.f, 1.f, -1.f), glm::vec3(0.f, 0.f, 1.f), glm::vec2(2.f, 0.f) },
{ se::Vectorf(-1.f, 1.f, -1.f), glm::vec3(1.f, 1.f, 0.f), glm::vec2(3.f, 0.f) },
{ se::Vectorf(-1, -1, -1), glm::vec3(1.f, 1.f, 0.f), glm::vec2(4.f, 0.f) },
{ se::Vectorf(-1, -1, 1), glm::vec3(1.f, 0.f, 0.f), glm::vec2(0.f, 1.f) },
{ se::Vectorf(1, -1, 1), glm::vec3(0.f, 1.f, 0.f), glm::vec2(1.f, 1.f) },
{ se::Vectorf(1, 1, 1), glm::vec3(0.f, 0.f, 1.f), glm::vec2(2.f, 1.f) },
{ se::Vectorf(-1, 1, 1), glm::vec3(1.f, 1.f, 0.f), glm::vec2(3.f, 1.f) },
{ se::Vectorf(-1, -1, 1), glm::vec3(1.f, 1.f, 0.f), glm::vec2(4.f, 1.f) },
{ se::Vectorf(-1, 1, -1), glm::vec3(1.f, 0.f, 0.f), glm::vec2(0.f, -1.f) },
{ se::Vectorf(1, 1, -1), glm::vec3(0.f, 1.f, 0.f), glm::vec2(1.f, -1.f) },
{ se::Vectorf(-1, 1, 1), glm::vec3(1.f, 0.f, 0.f), glm::vec2(0.f, 2.f) },
{ se::Vectorf(1, 1, 1), glm::vec3(0.f, 1.f, 0.f), glm::vec2(1.f, 2.f) },
};
std::vector<GLuint> cube_indicies = {
0, 1, 5, 5, 1, 6,
1, 2, 6, 6, 2, 7,
2, 3, 7, 7, 3, 8,
3, 4, 8, 8, 4, 9,
10, 11, 0, 0, 11, 1,
5, 6, 12, 12, 6, 13
};
std::unordered_map<aiTextureType, std::vector<std::shared_ptr<se::gfx::Texture>>> textures;
textures.emplace(white_texture.type, std::vector<std::shared_ptr<se::gfx::Texture>>{ std::make_shared<se::gfx::Texture>(white_texture) });
se::gfx::Material white_material(textures, 1.f, 0.f, 0.f, 0.f, 0.f);
auto camera = std::make_shared<se::Camera>(game.get_window(), core_shader, 70, glm::vec3(0, 0, 0));
game.add_event(camera);
// Create a renderer
auto renderer = std::make_shared<se::gfx::Renderer>(game.get_window(), core_shader);
auto renderer = std::make_shared<se::gfx::Renderer>(game.get_window(), core_shader, camera);
renderer->initialize();
game.add_renderable(renderer);
// Create a Scene and give it the renderer
auto scene = std::make_shared<se::Scene>(renderer);
game.add_event(scene);
se::ecs::Entity other_e = scene->create_entity();
other_e.add_component<se::ModelComponent>("examples/dev_testing/resources/transparent_window.fbx", simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE | simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_TRANSPARENT);
auto &other_transform = other_e.add_component<se::TransformComponent>();
other_transform.translate(5.f, 0.5f, 1.f);
// Create an Entity in the Scene and add components to it.
se::ecs::Entity entity = scene->create_entity();
//entity.add_component<se::ModelComponent>("examples/dev_testing/resources/dragon.obj");
//entity.add_component<se::ModelComponent>("examples/dev_testing/resources/stall.obj");
// Backpack model required vertically flipped texture coords.
auto& model_comp = entity.add_component<se::ModelComponent>("examples/dev_testing/resources/backpack/backpack.obj");
model_comp.model.vertically_flip_tex_coords();
// entity.add_component<se::ModelComponent>("examples/dev_testing/resources/planks/planks.fbx", simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE);
//entity.add_component<se::ModelComponent>("examples/dev_testing/resources/bricks/bricks.fbx", simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE);
entity.add_component<se::ModelComponent>("examples/dev_testing/resources/transparent_window.fbx", simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE | simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_TRANSPARENT);
//entity.add_component<se::ModelComponent>("examples/dev_testing/resources/scientist/scientist.fbx");
//entity.add_component<se::ModelComponent>("examples/dev_testing/resources/paradigm/paradigm.fbx");
//entity.add_component<se::RotatingComponent>();
auto& transform_comp = entity.add_component<se::TransformComponent>();
transform_comp.translate(15.f, -8.f, 0.f);
/* transform_comp.scale(0.1f);
transform_comp.rotate_z(-90.f);*/
transform_comp.rotate_x(-90.f);
auto &transform_comp = entity.add_component<se::TransformComponent>();
transform_comp.translate(4.f, 0.f, 0.f);
se::ecs::Entity brick_e = scene->create_entity();
brick_e.add_component<se::ModelComponent>("examples/dev_testing/resources/bricks/bricks.fbx", simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE);
auto &brick_transf = brick_e.add_component<se::TransformComponent>();
brick_transf.translate(6.f, -0.5f, 1.f);
// Create the entity and add the model component to it.
/* auto entity = std::make_shared<simpleengine::Entity>();
entity->add_component<se::MeshComponent>(cube_vertices, cube_indicies, white_material, true);
entity->translate(3.5f, 0.f, 0.f); */
/* auto entity = std::make_shared<simpleengine::Entity>();
entity->add_component<se::ModelComponent>("examples/dev_testing/resources/dragon.obj");
entity->translate(12.f, -4.f, 0.f); */
auto camera = std::make_shared<se::Camera>(game.get_window(), core_shader, 70, glm::vec3(0, 0, 0));
game.add_event(camera);
auto light = std::make_shared<se::gfx::Light>(core_shader, glm::vec3(0.f, 0.f, 0.f), glm::vec3(1.f, 1.f, 1.f));
game.add_event(light);
auto fps_counter = std::make_shared<FPSCounterEvent>();
game.add_event(fps_counter);
game.set_enable_vsync(true);
//game.set_fps_limit(120);
// game.set_fps_limit(120);
int res = game.run();
renderer->destroy();

View File

@ -18,8 +18,8 @@ namespace simpleengine::gfx {
virtual void update(const float& delta_time) override {
shader.use();
shader.set_uniform_float_vec3("light_position", position, false);
shader.set_uniform_float_vec3("light_color", color, false);
shader.set_uniform_float_vec3("u_light_position", position, false);
shader.set_uniform_float_vec3("u_light_color", color, false);
shader.unuse();
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "rendering_type.h"
#include "texture.h"
#include <assimp/material.h>
@ -10,17 +11,34 @@
namespace simpleengine::gfx {
class Material {
public:
RenderingType rendering_type;
// TODO: Only one of each texture type.
std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> textures;
float ambient_scalar;
float diffuse_scalar;
float specular_scalar;
float shine;
float reflectivity;
float ambient_strength;
float diffuse_strength;
Material(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> textures, float shine = 1.f, float reflectivity = 0.f, float specular_scalar = 0.f, float ambient_scalar = 0.f, float diffuse_scalar = 0.f) :
textures(textures), ambient_scalar(ambient_scalar), diffuse_scalar(diffuse_scalar), specular_scalar(specular_scalar),
shine(shine), reflectivity(reflectivity) {
/**
* @brief This strengthens the brightness of a specular highlight.
*
*/
float specular_strength;
/**
* @brief The shininess value of the highlight. (Radius of specular highlight?)
* The higher the shininess value of an object, the more it properly reflects the light
* instead of scattering it all around and thus the smaller the highlight becomes.
*
* The shader multiplies this by 32 to get the specular highlight.
*
*/
float shine_factor;
Material(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> textures, RenderingType rendering_type, float shine = 1.f,
float specular_scalar = 1.f, float ambient_scalar = 1.f, float diffuse_scalar = 1.f) : textures(textures), rendering_type(rendering_type),
ambient_strength(ambient_scalar), diffuse_strength(diffuse_scalar), specular_strength(specular_scalar), shine_factor(shine) {
}
};

View File

@ -8,6 +8,7 @@
#include "../renderable.h"
#include "../transformable.h"
#include "material.h"
#include "../vector.h"
#include <optional>
#include <vector>
@ -23,6 +24,9 @@ namespace simpleengine::gfx {
std::vector<LitVertex> vertices;
std::vector<GLuint> indicies;
std::vector<simpleengine::Vectorf> tangents;
gfx::VBO tangent_vbo;
bool are_buffers_created = false;
gfx::VBO ebo;
gfx::VBO vbo;
@ -30,7 +34,7 @@ namespace simpleengine::gfx {
Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material);
Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies = std::vector<GLuint>(), std::optional<Material> material = std::nullopt);
Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao);
Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao, gfx::VBO tangent_vbo);
virtual void destroy() override;
@ -45,5 +49,12 @@ namespace simpleengine::gfx {
*
*/
void calculate_normals();
/**
* @brief Calculate tangents and bi-tangents (tangent space) for the model.
*
* @note This **will** overwrite the existing tangents and bi-tangents.
*/
void calculate_tangents();
};
}

View File

@ -2,6 +2,7 @@
#include "mesh.h"
#include "simpleengine/gfx/texture.h"
#include "../vector.h"
#include <assimp/material.h>
#include <assimp/mesh.h>
@ -14,6 +15,8 @@ namespace simpleengine::gfx {
MdlProcFlag_NONE = 0b00000000,
MdlProcFlag_FLIP_TEX_COORDS_VERTICALLY = 0b00000001,
MdlProcFlag_FLIP_TEX_COORDS_HORIZONTALLY = 0b00000010,
MdlProcFlag_CALCULATE_TANGENT_SPACE = 0b00000100,
MdlProcFlag_TRANSPARENT = 0b00001000,
};
/**
@ -39,12 +42,12 @@ namespace simpleengine::gfx {
std::unordered_map<aiTextureType, std::vector<Texture>> load_all_textures(aiMaterial* material);
std::vector<std::shared_ptr<Texture>> load_material_texture(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiMaterial* material, aiTextureType type);
protected:
void post_process();
public:
void vertically_flip_tex_coords();
void horizontally_flip_tex_coords();
void calculate_tangents();
};
}

View File

@ -1,36 +1,45 @@
#pragma once
#include "../camera.h"
#include "shader.h"
#include "simpleengine/gfx/mesh.h"
#include "../renderable.h"
#include "rendering_type.h"
#include <unordered_map>
#include <vector>
#include <queue>
#include <map>
namespace simpleengine::gfx {
class Mesh;
class RenderingJob {
public:
gfx::Mesh& rendering_mesh;
RenderingType rendering_type;
gfx::Mesh* rendering_mesh;
glm::mat4 transform_mat;
RenderingJob(gfx::Mesh& mesh, glm::mat4 position) : rendering_mesh(mesh), transform_mat(position) {
}
RenderingJob(RenderingType rendering_type, gfx::Mesh& mesh, glm::mat4 position);
};
class Renderer : public simpleengine::Renderable {
private:
GLFWwindow* window;
protected:
public:
std::queue<RenderingJob> transparent_render_queue;
std::queue<RenderingJob> other_render_queue;
std::queue<RenderingJob> rendering_queue;
gfx::Shader shader;
std::shared_ptr<Camera> camera;
Renderer(GLFWwindow* window, gfx::Shader shader);
Renderer(GLFWwindow* window, GLuint shader_program);
Renderer(GLFWwindow* window, gfx::Shader shader, std::shared_ptr<Camera> camera);
Renderer(GLFWwindow* window, GLuint shader_program, std::shared_ptr<Camera> camera);
void enable_debug();
virtual void sort_jobs();
virtual void queue_job(RenderingType rendering_type, gfx::Mesh& mesh, glm::mat4 position);
virtual void queue_job(RenderingJob job);
virtual void create_job_buffers(RenderingJob& job);
@ -40,5 +49,16 @@ namespace simpleengine::gfx {
virtual void update(const float& delta_time) override;
virtual void render() override;
/**
* @brief Renders a single job.
*
* @param job The job that will be rendered.
* @return true if the job was rendered successfully.
* @return false if there was an error when trying to render the job.
*/
virtual bool render_job(const RenderingJob& job);
virtual void render_job_queue(std::queue<RenderingJob>& queue);
virtual void render_job_queue(std::map<float, RenderingJob, std::greater<>>& queue);
};
}

View File

@ -0,0 +1,8 @@
#pragma once
namespace simpleengine::gfx {
enum RenderingType {
RendType_OPAQUE = 0,
RendType_TRANSPARENT = 1,
};
}

View File

@ -23,17 +23,30 @@ namespace simpleengine::gfx {
TexFlags_FLIP_VERTICALLY = 0b00000010,
TexFlags_FLIP_HORIZONTALLY = 0b00000100,
TexFlags_MIPMAP = 0b00001000,
/* TexFlags_NO_COLOR = 0b00010000,
TexFlags_RGB = 0b00100000,
TexFlags_RGBA = 0b01000000, */
};
class Texture {
private:
unsigned char* img_data; // TODO Free this if its not used anymore
//unsigned char* img_data; // TODO Free this if its not used anymore
unsigned int texture_id;
unsigned int image_type_gl;
Texture() = default;
public:
/**
* @brief The default Texture flags including the color.
*
* The default flags are `TexFlags_IMG_2D | TexFlags_MIPMAP`
*
* @see simpleengine::gfx::Texture::default_flags_no_color
*
*/
static constexpr int default_flags = TexFlags_IMG_2D | TexFlags_MIPMAP;
int height;
int width;
int channels;
@ -47,8 +60,7 @@ namespace simpleengine::gfx {
* @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture.
*/
Texture(const char* path, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = TextureFlags::TexFlags_IMG_2D |
TextureFlags::TexFlags_MIPMAP);
Texture(const char* path, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = Texture::default_flags);
/**
* @brief Construct a new Texture object from the loaded file buffer.
@ -58,8 +70,7 @@ namespace simpleengine::gfx {
* @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture.
*/
Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = TextureFlags::TexFlags_IMG_2D |
TextureFlags::TexFlags_MIPMAP);
Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = Texture::default_flags);
/**
* @brief Construct a new Texture object from the loaded file buffer.
@ -68,8 +79,7 @@ namespace simpleengine::gfx {
* @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture.
*/
Texture(std::vector<unsigned char> buffer, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = TextureFlags::TexFlags_IMG_2D |
TextureFlags::TexFlags_MIPMAP);
Texture(std::vector<unsigned char> buffer, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = Texture::default_flags);
static Texture white_texture();

View File

@ -51,6 +51,10 @@ namespace simpleengine::gfx {
glBindVertexArray(handle);
}
void unbind() const {
glBindVertexArray(0);
}
// TODO: Fix this.
void enable_attrib(const VBO& vbo, GLuint index, GLint size, GLenum type, GLsizei stride, size_t offset, bool should_bind = true) const {
if (should_bind) {

View File

@ -40,6 +40,10 @@ namespace simpleengine::gfx {
glBindBuffer(type, handle);
}
void unbind() const {
glBindBuffer(type, 0);
}
void buffer(void *data, size_t offset, size_t size) {
bind();
glBufferData(type, size - offset, data, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);

View File

@ -26,22 +26,21 @@ namespace simpleengine {
glm::vec3 color;
glm::vec2 tex_coord;
glm::vec3 normal;
float texture_id = -1.f;
LitVertex() = default;
LitVertex(simpleengine::Vectorf position, glm::vec3 color, glm::vec2 tex_coord, int texture_id = -1.f) :
position(position), color(color), tex_coord(tex_coord), normal(glm::vec3(0.f)), texture_id((float) texture_id) {
position(position), color(color), tex_coord(tex_coord), normal(glm::vec3(0.f)) {
}
LitVertex(simpleengine::Vectorf position, glm::vec3 color, glm::vec2 tex_coord, glm::vec3 normal, int texture_id = -1.f) :
position(position), color(color), tex_coord(tex_coord), normal(normal), texture_id((float) texture_id) {
position(position), color(color), tex_coord(tex_coord), normal(normal) {
}
LitVertex(simpleengine::Vectorf position, glm::vec2 tex_coord, glm::vec3 normal, int texture_id = -1.f) :
position(position), color(glm::vec3(1.f)), tex_coord(tex_coord), normal(normal), texture_id((float) texture_id) {
position(position), color(glm::vec3(1.f)), tex_coord(tex_coord), normal(normal) {
}
};

View File

@ -3,56 +3,94 @@
in vec3 vs_position;
in vec3 vs_color;
in vec3 vs_normal;
in vec3 vs_world_normal; // Normal in world space
in vec2 vs_texcoord;
flat in float vs_tex_id; // < 0 is reserved for solid colored objects.
in mat4 vs_transform;
in vec3 vs_to_light;
in vec3 vs_to_camera;
uniform sampler2D u_textures[16];
uniform float u_texture_shine[16];
uniform float u_texture_reflectivity[16];
in vec3 vs_view_pos;
in vec3 vs_light_pos;
in vec3 vs_frag_pos;
uniform vec3 light_color;
//in mat3 vs_tbn;
in vec3 vs_tangent_light_pos;
in vec3 vs_tangent_view_pos;
in vec3 vs_tangent_frag_pos;
const int SAMP_DIFFUSE = 0;
const int SAMP_SPECULAR = 1;
struct Material {
sampler2D diffuse;
bool has_specular_map;
sampler2D specular_map;
// TODO: Make Optional
bool has_normal_map;
sampler2D normal_map;
float ambient_strength;
float diffuse_strength;
float specular_strength;
float shine_factor;
};
uniform Material u_material;
uniform vec3 u_light_color;
out vec4 fs_color;
vec3 calculate_specular(vec3 unit_normal, float shine_damper, float reflectivity);
vec3 calculate_lighting();
void main() {
// Lighting
vec3 unit_normal = normalize(vs_normal);
vec3 unit_light_vector = normalize(vs_to_light);
// Skip completely transparent fragments.
/* vec4 diffuse = texture(u_material.diffuse, vs_texcoord);
if (diffuse.a < 0.1) {
discard;
} */
float dot_prod = dot(unit_normal, unit_light_vector);
float brightness = max(dot_prod, 0.f);
vec3 diffuse = brightness * light_color;
vec3 lighting = calculate_lighting();
if (vs_tex_id > -1) {
int id = int(vs_tex_id);
float shine_damper = u_texture_shine[id];
float reflectivity = u_texture_reflectivity[id];
vec3 final_specular = calculate_specular(unit_normal, shine_damper, reflectivity);
fs_color = vec4(diffuse, 1.f) * texture(u_textures[id], vs_texcoord) + vec4(final_specular, 1.f);
} else {
fs_color = vec4(vs_color, 1.f); // We don't add any reflectivity to solid colored vectors.
}
fs_color = vec4(lighting, 1.f) * texture(u_material.diffuse, vs_texcoord);
}
vec3 calculate_specular(vec3 unit_normal, float shine_damper, float reflectivity) {
vec3 final_specular = vec3(0.f);
if (reflectivity > 0) {
vec3 unit_vector_to_camera = normalize(vs_to_camera);
vec3 light_direction = -unit_vector_to_camera;
vec3 reflected_light_dir = reflect(light_direction, unit_normal);
float specular_factor = dot(reflected_light_dir, unit_vector_to_camera);
specular_factor = max(specular_factor, 0.f);
float damped_specular = pow(specular_factor, shine_damper);
final_specular = damped_specular * reflectivity * light_color;
vec3 calculate_lighting() {
vec3 normal = vec3(0.f, 0.f, 1.f); // default tangent-space normal
// Check if the normal map is set before trying to apply it.
if (u_material.has_normal_map) {
normal = texture(u_material.normal_map, vs_texcoord).rgb;
// transform normal vector to range [-1,1]
normal = normalize(normal * 2.f - 1.f); // this normal is in tangent space
}
return final_specular;
// Get diffuse color
vec3 diffuse_map = texture(u_material.diffuse, vs_texcoord).rgb;
// Ambient lighting
vec3 ambient = 0.1f * u_material.ambient_strength * diffuse_map;
// Diffuse lighting
vec3 light_dir = normalize(vs_tangent_light_pos - vs_tangent_frag_pos);
float diff = max(dot(light_dir, normal), 0.f);
vec3 diffuse = diff * u_material.diffuse_strength * diffuse_map;
// Specular lighting
vec3 view_dir = normalize(vs_tangent_view_pos - vs_tangent_frag_pos);
vec3 reflect_dir = reflect(-light_dir, normal);
vec3 halfway_dir = normalize(light_dir + view_dir);
float spec = pow(max(dot(normal, halfway_dir), 0.f), 32.f * u_material.shine_factor);
vec3 specular = vec3(0.2f * u_material.specular_strength) * spec;
// Check if the specular map is set before trying to apply it.
if (u_material.has_specular_map) {
specular = specular * texture(u_material.specular_map, vs_texcoord).r;
}
return ambient + diffuse + specular;
}

View File

@ -4,38 +4,62 @@ layout (location = 0) in vec3 vertex_position;
layout (location = 1) in vec3 vertex_color;
layout (location = 2) in vec3 vertex_normal;
layout (location = 3) in vec2 vertex_texcoord;
layout (location = 4) in float vertex_tex_id;
// TODO: Make these optional
layout (location = 4) in vec3 vertex_tangent;
out vec3 vs_position;
out vec3 vs_color;
out vec3 vs_normal;
out vec3 vs_world_normal; // normal in world space
out vec2 vs_texcoord;
flat out float vs_tex_id;
out mat4 vs_transform;
out vec3 vs_to_light;
out vec3 vs_to_camera;
uniform mat4 transform_matrix;
uniform mat4 view_matrix;
uniform mat4 projection_matrix;
uniform vec3 light_position;
out vec3 vs_view_pos;
out vec3 vs_light_pos;
out vec3 vs_frag_pos;
//out mat3 vs_tbn;
out vec3 vs_tangent_light_pos;
out vec3 vs_tangent_view_pos;
out vec3 vs_tangent_frag_pos;
uniform mat4 u_transform_matrix;
uniform mat4 u_view_matrix;
uniform mat4 u_projection_matrix;
uniform vec3 u_view_pos;
uniform vec3 u_light_position;
void main() {
vec4 world_pos = (transform_matrix * vec4(vertex_position, 1.f));
vec4 world_pos = (u_transform_matrix * vec4(vertex_position, 1.f));
// Directly pass things to the fragment shader.
vs_position = world_pos.xyz;
vs_transform = transform_matrix;
vs_transform = u_transform_matrix;
vs_texcoord = vertex_texcoord;
vs_color = vertex_color;
vs_tex_id = vertex_tex_id;
gl_Position = u_projection_matrix * u_view_matrix * world_pos;
// Apply transform matrix to normal.
vs_normal = (u_transform_matrix * vec4(vertex_normal, 0.f)).xyz;
gl_Position = projection_matrix * view_matrix * world_pos;
vs_view_pos = u_view_pos;
vs_light_pos = u_light_position;
vs_frag_pos = vec3(u_transform_matrix * vec4(vertex_position, 1.f));
vs_world_normal = mat3(transpose(inverse(u_transform_matrix))) * vertex_normal; // TODO: Do this calculation on the CPU then send to GPU via a uniform
vs_normal = (transform_matrix * vec4(vertex_normal, 0.f)).xyz;
vs_to_light = light_position - world_pos.xyz;
vs_to_camera = (inverse(view_matrix) * vec4(0.f, 0.f, 0.f, 1.f)).xyz - world_pos.xyz;
// Calculate tangent space stuff.
mat3 normal_matrix = transpose(inverse(mat3(u_transform_matrix)));
vec3 T = normalize(normal_matrix * vertex_tangent);
vec3 N = normalize(normal_matrix * vertex_normal);
T = normalize(T - dot(T, N) * N);
vec3 B = cross(N, T);
mat3 TBN = transpose(mat3(T, B, N));
vs_tangent_light_pos = TBN * u_light_position;
vs_tangent_view_pos = TBN * u_view_pos;
vs_tangent_frag_pos = TBN * vs_frag_pos;
}

View File

@ -63,19 +63,19 @@ namespace simpleengine {
}
if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
rotation.z += camera_speed * .3;
rotation.z += camera_speed * .4;
}
if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
rotation.z -= camera_speed * .3;
rotation.z -= camera_speed * .4;
}
if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) {
rotation.y -= camera_speed * .3;
rotation.y -= camera_speed * .4;
}
if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
rotation.y += camera_speed * .3;
rotation.y += camera_speed * .4;
}
// Limit the pitch of the camera.
@ -96,8 +96,9 @@ namespace simpleengine {
view_matrix = glm::lookAt(position, position + camera_front, camera_up);
shader.use();
shader.set_uniform_matrix_4f("view_matrix", view_matrix, false);
shader.set_uniform_matrix_4f("projection_matrix", projection_matrix, false);
shader.set_uniform_float_vec3("u_view_pos", position, false);
shader.set_uniform_matrix_4f("u_view_matrix", view_matrix, false);
shader.set_uniform_matrix_4f("u_projection_matrix", projection_matrix, false);
shader.unuse();
}
}

View File

@ -48,9 +48,7 @@ simpleengine::Game::Game(int w, int h, const std::string& window_name, const int
}
void simpleengine::Game::enable_default_gl_options() const {
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
//glFrontFace(GL_CW);
update_enabled_vsync();

View File

@ -1,23 +1,26 @@
#include "gfx/mesh.h"
#include "gfx/vbo.h"
#include "vector.h"
#include <optional>
namespace simpleengine::gfx {
Mesh::Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material) :
material(std::make_optional(material)), vertices(vertices), indicies(indicies),
vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), ebo(gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false)),
vao(gfx::VAO::init()) {
vao(gfx::VAO::init()), tangent_vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)) {
}
Mesh::Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, std::optional<Material> material) :
material(material), vertices(vertices), indicies(indicies),
vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), ebo(gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false)),
vao(gfx::VAO::init()) {
vao(gfx::VAO::init()), tangent_vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)) {
}
Mesh::Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao) :
vertices(vertices), indicies(indicies), material(material), ebo(ebo), vbo(vbo), vao(vao) {
Mesh::Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao,
gfx::VBO tangent_vbo) :
vertices(vertices), indicies(indicies), material(material), ebo(ebo), vbo(vbo), vao(vao), tangent_vbo(tangent_vbo) {
}
@ -76,4 +79,40 @@ namespace simpleengine::gfx {
vertices[i].normal = normals[i];
}
}
void Mesh::calculate_tangents() {
// Resize the tangents vector, they will overwritten,
// so no need to clear the vector.
tangents.resize(vertices.size());
for (int i = 0; i < indicies.size(); i += 3) {
int index0 = indicies[i];
int index1 = indicies[i+1];
int index2 = indicies[i+2];
LitVertex& lit_vertex0 = vertices[index0];
LitVertex& lit_vertex1 = vertices[index1];
LitVertex& lit_vertex2 = vertices[index2];
glm::vec3 pos0 = lit_vertex0.position;
glm::vec3 pos1 = lit_vertex1.position;
glm::vec3 pos2 = lit_vertex2.position;
// Edges of the triangle : postion delta
glm::vec3 delta_pos1 = pos1 - pos0;
glm::vec3 delta_pos2 = pos2 - pos0;
// UV delta
glm::vec2 delta_uv1 = lit_vertex1.tex_coord - lit_vertex0.tex_coord;
glm::vec2 delta_uv2 = lit_vertex2.tex_coord - lit_vertex0.tex_coord;
float r = 1.0f / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
glm::vec3 tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y)*r;
//glm::vec3 bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x)*r;
tangents[index0] = tangent;
tangents[index1] = tangent;
tangents[index2] = tangent;
}
}
}

View File

@ -1,5 +1,6 @@
#include "gfx/model.h"
#include "gfx/material.h"
#include "gfx/rendering_type.h"
#include "gfx/texture.h"
#include "vector.h"
@ -10,6 +11,7 @@
#include <optional>
#include <sstream>
#include <stdexcept>
#include <unordered_map>
#include <vector>
@ -32,6 +34,10 @@ namespace simpleengine::gfx {
void Model::load_model(std::string path) {
Assimp::Importer importer;
if (model_processing_flags & ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE) {
additional_assimp_flags |= aiProcess_CalcTangentSpace;
}
// assimp post processing options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html
const aiScene *scene = importer.ReadFile(path, additional_assimp_flags | aiProcess_Triangulate | aiProcess_FlipUVs);
@ -63,27 +69,33 @@ namespace simpleengine::gfx {
gfx::Mesh Model::process_mesh(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiMesh* mesh, const aiScene* scene) {
std::vector<LitVertex> vertices;
std::vector<unsigned int> indices;
std::vector<unsigned int> indicies;
std::vector<Vectorf> tangents;
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
LitVertex vertex;
vertex.color = glm::vec3(1.f);
vertex.texture_id = 0;
simpleengine::Vectorf position(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);
vertex.position = position;
// Only process normals if they exist.
if (mesh->HasNormals()) {
glm::vec3 normal(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);
vertex.normal = normal;
}
if (mesh->mTextureCoords[0]) {
// Only process texture coords if they exist.
if (mesh->HasTextureCoords(0)) {
glm::vec2 tex_coord(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y);
vertex.tex_coord = tex_coord;
}
if (mesh->HasTangentsAndBitangents()) {
tangents.emplace_back(mesh->mTangents[i].x, mesh->mTangents[i].y, mesh->mTangents[i].z);
}
vertices.push_back(vertex);
}
@ -92,15 +104,21 @@ namespace simpleengine::gfx {
aiFace face = mesh->mFaces[i];
for (int j = 0; j < face.mNumIndices; j++) {
indices.push_back(face.mIndices[j]);
indicies.push_back(face.mIndices[j]);
}
}
// Get the rendering type
RenderingType rendering_type = RenderingType::RendType_OPAQUE;
if (model_processing_flags & ModelProcessingFlags::MdlProcFlag_TRANSPARENT) {
rendering_type = RenderingType::RendType_TRANSPARENT;
}
// Create a default material and white texture.
auto white_texture = gfx::Texture::white_texture();
std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> default_textures;
default_textures.emplace(white_texture.type, std::vector<std::shared_ptr<Texture>>{ std::make_shared<Texture>(white_texture) });
gfx::Material mat(default_textures, 1.f, 0.f, 0.f, 0.f, 0.f);
gfx::Material mat(default_textures, rendering_type);
if (mesh->mMaterialIndex >= 0) {
std::cout << "TODO: Process model materials!" << std::endl;
@ -109,14 +127,30 @@ namespace simpleengine::gfx {
aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
// Load Diffuse texture maps
std::vector<std::shared_ptr<Texture>> diffuse_maps = load_material_texture(processed_textures, material, aiTextureType_DIFFUSE);
if (!diffuse_maps.empty()) textures.emplace(aiTextureType_DIFFUSE, diffuse_maps);
aiTextureType loading_type = aiTextureType_DIFFUSE;
std::vector<std::shared_ptr<Texture>> diffuse_maps = load_material_texture(processed_textures, material, loading_type);
if (!diffuse_maps.empty()) textures.emplace(loading_type, diffuse_maps);
// Load specular texture maps
loading_type = aiTextureType_SPECULAR;
std::vector<std::shared_ptr<Texture>> spec_maps = load_material_texture(processed_textures, material, loading_type);
if (!spec_maps.empty()) textures.emplace(loading_type, spec_maps);
// Load normals texture maps
loading_type = aiTextureType_NORMALS;
std::vector<std::shared_ptr<Texture>> normal_maps = load_material_texture(processed_textures, material, loading_type);
if (!normal_maps.empty()) {
textures.emplace(loading_type, normal_maps);
// Force calculation of tangent space.
model_processing_flags |= MdlProcFlag_CALCULATE_TANGENT_SPACE;
}
// TODO Handle other types of texture maps
if (!textures.empty()) {
// TODO: Find a way to let the user set the scalars.
mat = Material(textures, 1.f, 0.f, 0.f, 0.f, 0.f);
mat = Material(textures, rendering_type);
// Add `textures` into the `processed_textures` list.
for (const auto& pair : textures) {
@ -144,12 +178,16 @@ namespace simpleengine::gfx {
}
}
Mesh se_mesh(vertices, indices, mat);
Mesh se_mesh(vertices, indicies, mat);
if (!mesh->HasNormals()) {
se_mesh.calculate_normals();
}
if (mesh->HasTangentsAndBitangents()) {
se_mesh.tangents = tangents;
}
return se_mesh;
}
@ -185,7 +223,7 @@ namespace simpleengine::gfx {
ss << model_directory << "/" << texture_path;
std::string full_path = ss.str();
Texture texture(full_path.c_str(), type, /* TextureFlags::TexFlags_FLIP_VERTICALLY | */ TextureFlags::TexFlags_IMG_2D | TextureFlags::TexFlags_MIPMAP);
Texture texture(full_path.c_str(), type);
texture.path = texture_path;
textures.emplace_back(std::make_shared<Texture>(texture));
@ -210,4 +248,50 @@ namespace simpleengine::gfx {
}
}
}
void Model::calculate_tangents() {
for (auto& mesh : meshes) {
// TODO: Find a way to check if normals and UVs are set before calculating tangents
mesh.calculate_tangents();
}
}
/* std::vector<std::pair<simpleengine::Vectorf, simpleengine::Vectorf>> calculate_tangent_space(
std::vector<LitVertex> vertices, std::vector<unsigned int> indices) {
std::vector<std::pair<simpleengine::Vectorf, simpleengine::Vectorf>> tangents;
tangents.resize(vertices.size());
for (int i = 0; i < indices.size(); i += 3) {
int index0 = indices[i];
int index1 = indices[i+1];
int index2 = indices[i+2];
LitVertex& lit_vertex0 = vertices[index0];
LitVertex& lit_vertex1 = vertices[index1];
LitVertex& lit_vertex2 = vertices[index2];
glm::vec3 pos0 = lit_vertex0.position;
glm::vec3 pos1 = lit_vertex1.position;
glm::vec3 pos2 = lit_vertex2.position;
// Edges of the triangle : postion delta
glm::vec3 delta_pos1 = pos1 - pos0;
glm::vec3 delta_pos2 = pos2 - pos0;
// UV delta
glm::vec2 delta_uv1 = lit_vertex1.tex_coord - lit_vertex0.tex_coord;
glm::vec2 delta_uv2 = lit_vertex2.tex_coord - lit_vertex0.tex_coord;
float r = 1.0f / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
glm::vec3 tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y)*r;
glm::vec3 bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x)*r;
tangents[0] = {tangent, bitangent};
tangents[1] = {tangent, bitangent};
tangents[2] = {tangent, bitangent};
}
return tangents;
} */
} // namespace simpleengine::gfx

View File

@ -5,28 +5,31 @@
#include "ecs/component/mesh_component.h"
#include "ecs/component/model_component.h"
#include "vector.h"
#include <algorithm>
#include <assimp/material.h>
#include <functional>
#include <glm/geometric.hpp>
// TODO: Check if initialized before trying to do stuff
namespace simpleengine::gfx {
void create_mesh_buffers(simpleengine::gfx::Mesh& mesh);
void create_mesh_buffers(simpleengine::gfx::Mesh &mesh);
Renderer::Renderer(GLFWwindow* window, gfx::Shader shader): window(window), shader(shader) {
RenderingJob::RenderingJob(RenderingType rendering_type, gfx::Mesh &mesh, glm::mat4 position)
: rendering_type(rendering_type), rendering_mesh(&mesh), transform_mat(position) {}
}
Renderer::Renderer(GLFWwindow *window, gfx::Shader shader, std::shared_ptr<Camera> camera)
: window(window), shader(shader), camera(camera)/* , transparent_render_queue(CameraDistanceComparator(camera)) */ {}
Renderer::Renderer(GLFWwindow* window, GLuint shader_program): Renderer(window,
gfx::Shader(shader_program)) {
Renderer::Renderer(GLFWwindow *window, GLuint shader_program, std::shared_ptr<Camera> camera)
: Renderer(window, gfx::Shader(shader_program), camera) {}
}
void debug_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar* message, const void* userParam) {
void debug_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
const GLchar *message, const void *userParam) {
fprintf(stderr, "%s type = 0x%x, severity = 0x%x, message = %s\n",
(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
type, severity, message);
(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type, severity, message);
}
void Renderer::enable_debug() {
@ -34,24 +37,49 @@ namespace simpleengine::gfx {
glDebugMessageCallback(debug_message_callback, 0);
}
void Renderer::queue_job(RenderingJob job) {
RenderingJob& emplace = rendering_queue.emplace(job);
create_job_buffers(emplace);
void Renderer::sort_jobs() {
// Sort transparents
// std::sort()
}
void Renderer::create_job_buffers(RenderingJob& job) {
Mesh& rendering_mesh = job.rendering_mesh;
void Renderer::queue_job(RenderingType rendering_type, gfx::Mesh &mesh, glm::mat4 position) {
RenderingJob job(rendering_type, mesh, position);
if (!rendering_mesh.are_buffers_created) {
gfx::VBO& vbo = rendering_mesh.vbo;
gfx::VBO& ebo = rendering_mesh.ebo;
gfx::VAO& vao = rendering_mesh.vao;
this->queue_job(job);
}
void Renderer::queue_job(RenderingJob job) {
create_job_buffers(job);
switch (job.rendering_type) {
case RenderingType::RendType_TRANSPARENT: {
/* glm::vec3 pos = job.transform_mat[3];
float distance = glm::distance(pos, camera->position); */
this->transparent_render_queue.emplace(job);
break;
}
default:
this->other_render_queue.emplace(job);
break;
}
sort_jobs();
}
void Renderer::create_job_buffers(RenderingJob &job) {
Mesh *rendering_mesh = job.rendering_mesh;
if (!rendering_mesh->are_buffers_created) {
gfx::VBO &vbo = rendering_mesh->vbo;
gfx::VBO &ebo = rendering_mesh->ebo;
gfx::VAO &vao = rendering_mesh->vao;
vao.bind();
vbo.buffer(rendering_mesh.vertices.data(), 0, sizeof(LitVertex) * rendering_mesh.vertices.size());
vbo.buffer(rendering_mesh->vertices.data(), 0, sizeof(LitVertex) * rendering_mesh->vertices.size());
if (!rendering_mesh.indicies.empty()) {
ebo.buffer(rendering_mesh.indicies.data(), 0, rendering_mesh.indicies.size() * sizeof(GLuint));
if (!rendering_mesh->indicies.empty()) {
ebo.buffer(rendering_mesh->indicies.data(), 0, rendering_mesh->indicies.size() * sizeof(GLuint));
}
// Enable VAO attributes
@ -59,22 +87,30 @@ namespace simpleengine::gfx {
vao.enable_attrib(vbo, 1, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, color), false);
vao.enable_attrib(vbo, 2, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, normal), false);
vao.enable_attrib(vbo, 3, 2, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, tex_coord), false);
vao.enable_attrib(vbo, 4, 1, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, texture_id), false);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
rendering_mesh->tangent_vbo.buffer(rendering_mesh->tangents.data(), 0,
rendering_mesh->tangents.size() * sizeof(Vectorf));
vao.enable_attrib(rendering_mesh->tangent_vbo, 4, 3, GL_FLOAT, sizeof(Vectorf), 0, false);
rendering_mesh.are_buffers_created = true;
vbo.unbind();
vao.unbind();
rendering_mesh->are_buffers_created = true;
std::cout << "Created render job buffers" << std::endl;
}
}
void Renderer::update(const float& delta_time) {
}
void Renderer::update(const float &delta_time) {}
void Renderer::initialize() {
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
std::cout << "Base Renderer initialized" << std::endl;
}
@ -86,48 +122,112 @@ namespace simpleengine::gfx {
} */
}
void Renderer::render() {
bool Renderer::render_job(const RenderingJob &job) {
Mesh *mesh = job.rendering_mesh;
shader.set_uniform_matrix_4f("u_transform_matrix", job.transform_mat, false);
std::optional<Material> &material = mesh->material;
if (material.has_value()) {
shader.set_uniform_float("u_material.ambient_strength", material->ambient_strength, false);
shader.set_uniform_float("u_material.diffuse_strength", material->diffuse_strength, false);
shader.set_uniform_float("u_material.specular_strength", material->specular_strength, false);
shader.set_uniform_float("u_material.shine_factor", material->shine_factor, false);
// shader.set_uniform_float("u_material.reflect_factor", .1f, false);
auto diffuse_maps = material->textures.find(aiTextureType_DIFFUSE);
auto diffuse_map = diffuse_maps->second.front();
shader.set_uniform_int("u_material.diffuse", 0, false);
glActiveTexture(GL_TEXTURE0);
diffuse_map->bind();
// Apply the specular map if it exists
auto specular_maps = material->textures.find(aiTextureType_SPECULAR);
if (specular_maps != material->textures.end()) {
auto spec = specular_maps->second.front();
shader.set_uniform_int("u_material.has_specular_map", 1, false);
shader.set_uniform_int("u_material.specular_map", 1, false);
glActiveTexture(GL_TEXTURE1);
spec->bind();
} else {
shader.set_uniform_int("u_material.has_specular_map", 0, false);
}
// Apply the normal map if it exists
auto normal_maps = material->textures.find(aiTextureType_NORMALS);
if (normal_maps != material->textures.end()) {
auto normal = normal_maps->second.front();
shader.set_uniform_int("u_material.has_normal_map", 1, false);
shader.set_uniform_int("u_material.normal_map", 2, false);
glActiveTexture(GL_TEXTURE2);
normal->bind();
} else {
shader.set_uniform_int("u_material.has_normal_map", 0, false);
}
}
mesh->vao.bind();
if (mesh->indicies.empty()) {
glDrawArrays(GL_TRIANGLES, 0, mesh->vertices.size());
} else {
glDrawElements(GL_TRIANGLES, mesh->indicies.size(), GL_UNSIGNED_INT, 0);
}
return true;
}
void Renderer::render_job_queue(std::queue<RenderingJob> &rendering_queue) {
shader.use();
while (!rendering_queue.empty()) {
// Get the job from the queue, we'll remove it after we render.
RenderingJob& job = rendering_queue.front();
Mesh& mesh = job.rendering_mesh;
RenderingJob &job = rendering_queue.front();
shader.set_uniform_matrix_4f("transform_matrix", job.transform_mat, false);
bool res = this->render_job(job);
std::optional<Material>& material = mesh.material;
shader.set_uniform_int("u_textures", 0, false);
if (material.has_value()) {
shader.set_uniform_float("u_texture_shine", material->shine, false);
shader.set_uniform_float("u_texture_reflectivity", material->reflectivity, false);
int texture_count = 0;
auto diffuse_maps = material->textures.find(aiTextureType_DIFFUSE);
for (const auto& texture : diffuse_maps->second) {
// We can only bind to 16 textures at a time (indexes are 0-15)
if (texture_count >= 16) break;
glActiveTexture(GL_TEXTURE0 + texture_count);
glBindTextureUnit(texture_count, texture->get_texture_id());
texture_count++;
}
if (res) {
// Now we'll remove the job from the queue.
rendering_queue.pop();
}
mesh.vao.bind();
if (mesh.indicies.empty()) {
glDrawArrays(GL_TRIANGLES, 0, mesh.vertices.size());
} else {
glDrawElements(GL_TRIANGLES, mesh.indicies.size(), GL_UNSIGNED_INT, 0);
}
// Now we'll remove the job from the queue.
rendering_queue.pop();
}
shader.unuse();
}
}
void Renderer::render_job_queue(std::map<float, RenderingJob, std::greater<>>& rendering_queue) {
shader.use();
// Render each job then clear the queue
for (const auto& it : rendering_queue) {
this->render_job(it.second);
}
rendering_queue.clear();
shader.unuse();
}
void Renderer::render() {
// Render other (opaque) objects first
this->render_job_queue(other_render_queue);
// Render transparent objects
std::map<float, RenderingJob, std::greater<>> transparent_jobs;
while (!transparent_render_queue.empty()) {
RenderingJob& job = transparent_render_queue.front();
glm::vec3 pos = job.transform_mat[3];
float distance = glm::distance(pos, camera->position);
transparent_jobs.emplace(distance, job);
transparent_render_queue.pop();
}
this->render_job_queue(transparent_jobs);
}
} // namespace simpleengine::gfx

View File

@ -1,4 +1,5 @@
#include "gfx/texture.h"
#include <stdexcept>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
@ -23,8 +24,7 @@ namespace simpleengine::gfx {
stbi_set_flip_vertically_on_load(flip_vertically);
// Read 4 channels (RGBA)
img_data = stbi_load(path, &width, &height, &channels, 4);
unsigned char*img_data = stbi_load(path, &width, &height, &channels, 0);
if(!img_data) {
const char* failure = stbi_failure_reason();
std::cerr << "Failed to load texture! (" << failure << ")" << std::endl;
@ -32,13 +32,27 @@ namespace simpleengine::gfx {
}
std::cout << "Loaded image with a width of " << width << "px, a height of " << height << "px and " << channels << " channels" << std::endl;
glTexImage2D(image_type_gl, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data);
// Get the color type
int color_format = 0;
if (channels == 1) {
color_format = GL_RED;
} else if (channels == 3) {
color_format = GL_RGB;
} else if (channels == 4) {
color_format = GL_RGBA;
} else {
std::cerr << "Unknown texture color format with " << channels << " channels!" << std::endl;
throw std::runtime_error("Unknown texture color format!");
}
glTexImage2D(image_type_gl, 0, color_format, width, height, 0, color_format, GL_UNSIGNED_BYTE, img_data);
if (mipmap) {
glGenerateMipmap(image_type_gl);
}
stbi_set_flip_vertically_on_load(false);
stbi_image_free(img_data);
unbind();
}
@ -62,8 +76,7 @@ namespace simpleengine::gfx {
stbi_set_flip_vertically_on_load(flip_vertically);
// Read 4 channels (RGBA)
img_data = stbi_load_from_memory(buffer, buffer_length, &width, &height, &channels, 4);
unsigned char* img_data = stbi_load_from_memory(buffer, buffer_length, &width, &height, &channels, 0);
if(!img_data) {
const char* failure = stbi_failure_reason();
std::cerr << "Failed to load texture! (" << failure << ")" << std::endl;
@ -71,13 +84,27 @@ namespace simpleengine::gfx {
}
std::cout << "Loaded image with a width of " << width << "px, a height of " << height << "px and " << channels << " channels" << std::endl;
glTexImage2D(image_type_gl, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data);
// Get the color type
int color_format = 0;
if (channels == 1) {
color_format = GL_RED;
} else if (channels == 3) {
color_format = GL_RGB;
} else if (channels == 4) {
color_format = GL_RGBA;
} else {
std::cerr << "Unknown texture color format with " << channels << " channels!" << std::endl;
throw std::runtime_error("Unknown texture color format!");
}
glTexImage2D(image_type_gl, 0, color_format, width, height, 0, color_format, GL_UNSIGNED_BYTE, img_data);
if (mipmap) {
glGenerateMipmap(image_type_gl);
}
stbi_set_flip_vertically_on_load(false);
stbi_image_free(img_data);
unbind();
}
@ -103,7 +130,6 @@ namespace simpleengine::gfx {
texture.height = height;
texture.channels = 4;
texture.type = aiTextureType::aiTextureType_DIFFUSE;
texture.img_data = data;
glGenTextures(1, &texture.texture_id);
texture.bind();
@ -113,11 +139,11 @@ namespace simpleengine::gfx {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.img_data);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
texture.unbind();
free(data);
return texture;
}

View File

@ -19,12 +19,22 @@ namespace simpleengine {
// Is there a way these can be grouped?
registry.view<const TransformComponent, ModelComponent>().each([this](const TransformComponent& transform, ModelComponent& model_component) {
for (auto& mesh : model_component.model.meshes) {
renderer->queue_job(gfx::RenderingJob(mesh, transform.transform_matrix));
auto rendering_type = gfx::RenderingType::RendType_OPAQUE;
if (mesh.material) {
rendering_type = mesh.material->rendering_type;
}
renderer->queue_job(gfx::RenderingJob(rendering_type, mesh, transform.transform_matrix));
}
});
registry.view<const TransformComponent, MeshComponent>().each([this](const TransformComponent& transform, MeshComponent& mesh_component) {
renderer->queue_job(gfx::RenderingJob(mesh_component.mesh, transform.transform_matrix));
auto rendering_type = gfx::RenderingType::RendType_OPAQUE;
if (mesh_component.mesh.material) {
rendering_type = mesh_component.mesh.material->rendering_type;
}
renderer->queue_job(gfx::RenderingJob(rendering_type, mesh_component.mesh, transform.transform_matrix));
});
registry.view<TransformComponent, RotatingComponent>().each([this, &delta_time](TransformComponent& transform, RotatingComponent& rotating) {