Huge stuff. camera, input manager (mouse + keyboard), can move around scene now

This commit is contained in:
2026-05-04 00:41:34 -04:00
parent 9a8e618bbb
commit 3c756d6230
17 changed files with 624 additions and 119 deletions

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 4.1)
project(b_engine VERSION 0.0.1 LANGUAGES CXX C)
project(b_engine VERSION 0.0.2 LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 20)
@@ -26,17 +26,27 @@ add_executable(${PROJECT_NAME}
src/Mesh.h
src/Model.cpp
src/Model.h
src/Material.cpp
src/Material.h
src/ModelLoader.cpp
src/ModelLoader.h
src/ModelManager.cpp
src/ModelManager.h
src/ShaderProgram.cpp
src/ShaderProgram.h
src/Texture.h
src/TextureLoader.cpp
src/TextureLoader.h
src/TextureManager.cpp
src/TextureManager.h
src/ShaderManager.cpp
src/ShaderManager.h)
src/ShaderManager.h
src/FreeCamera.cpp
src/FreeCamera.h
src/InputManager.cpp
src/InputManager.h)
add_custom_target(copy_resources
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_CURRENT_SOURCE_DIR}/resources"
"${CMAKE_CURRENT_BINARY_DIR}/resources"
COMMENT "Copying resources..."
)
target_link_libraries(${PROJECT_NAME} glfw spdlog assimp glm)
target_include_directories(${PROJECT_NAME} PRIVATE ${ASSIMP_INCLUDE_INSTALL_DIR})

View File

@@ -10,7 +10,7 @@ Ke 0.0 0.0 0.0
Ni 1.450000
d 1.000000
illum 2
map_Kd backpack/diffuse.jpg
map_Bump backpack/normal.png
map_Ks backpack/specular.jpg
map_Kd diffuse.jpg
map_Bump normal.png
map_Ks specular.jpg

View File

@@ -6,7 +6,8 @@ in vec3 Position;
in vec2 TexCoord;
in vec3 Normal;
uniform sampler2D diffuse;
uniform sampler2D diffuseMap;
uniform sampler2D specularMap;
uniform vec3 viewPosition;
@@ -15,22 +16,30 @@ uniform vec3 phongDiffuse;
uniform vec3 phongSpecular;
uniform float phongShininess;
uniform vec3 lightPosition;
uniform vec3 lightDirection;
uniform vec3 lightAmbient;
uniform vec3 lightDiffuse;
uniform vec3 lightSpecular;
void main() {
vec4 diffuseColor = texture(diffuse, TexCoord);
vec3 diffuseColor = texture(diffuseMap, TexCoord).rgb;
vec3 specularColor = texture(specularMap, TexCoord).rgb;
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(-lightDirection);
vec3 viewDir = normalize(viewPosition - Position);
vec3 ambient = phongAmbient * vec3(diffuseColor);
vec3 ambient = lightAmbient * (diffuseColor * phongAmbient);
float diffImpact = max(dot(norm, lightDir), 0.0);
vec3 diffuse = (phongDiffuse * vec3(diffuseColor)) * diffImpact;
vec3 diffuse = lightDiffuse * (diffImpact * diffuseColor * phongDiffuse);
vec3 result = ambient + diffuse;
vec3 reflectDir = reflect(-lightDir, norm);
float effectiveShininess = max(phongShininess, 1.0);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), effectiveShininess);
vec3 specular = lightSpecular * (spec * specularColor * phongSpecular);
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}

99
src/FreeCamera.cpp Normal file
View File

@@ -0,0 +1,99 @@
//
// Created by slinky on 5/3/26.
//
#include "FreeCamera.h"
#include "glm/ext/matrix_transform.hpp"
#include "glm/ext/matrix_clip_space.hpp"
FreeCamera::FreeCamera(const glm::vec3 &pos, float pitch, float yaw, float aspectRatio) : _position(pos), _yaw(yaw), _pitch(pitch), _aspectRatio(aspectRatio) {
update_camera_vectors();
update_view();
update_projection();
}
const glm::mat4& FreeCamera::view() const {
return _view;
}
const glm::mat4& FreeCamera::projection() const {
return _projection;
}
const glm::vec3& FreeCamera::position() const {
return _position;
}
void FreeCamera::rotate_pitch(float offset) {
_pitch += offset;
if (_pitch < MIN_PITCH) {
_pitch = MIN_PITCH;
}
if (_pitch > MAX_PITCH) {
_pitch = MAX_PITCH;
}
update_camera_vectors();
update_view();
}
void FreeCamera::rotate_yaw(float offset) {
_yaw += offset;
update_camera_vectors();
update_view();
}
void FreeCamera::move(CAMERA_MOVEMENT direction, float offset) {
glm::vec3 finalDirection = {};
switch (direction) {
case FORWARD:
finalDirection = front;
break;
case BACKWARD:
finalDirection = -front;
break;
case LEFT:
finalDirection = -right;
break;
case RIGHT:
finalDirection = right;
break;
case UP:
finalDirection = up;
break;
case DOWN:
finalDirection = -up;
break;
}
finalDirection = glm::normalize(finalDirection);
_position += finalDirection * offset;
_view = glm::lookAt(_position, _position + front, up);
}
void FreeCamera::update_aspect_ratio(float aspectRatio) {
_aspectRatio = aspectRatio;
_projection = glm::perspective(glm::radians(75.f), _aspectRatio, 0.1f, 100.0f);
}
void FreeCamera::update_camera_vectors() {
front = {};
front.x = cos(glm::radians(_yaw)) * cos(glm::radians(_pitch));
front.y = sin(glm::radians(_pitch));
front.z = sin(glm::radians(_yaw)) * cos(glm::radians(_pitch));
right = glm::normalize(glm::cross(front, WORLD_UP));
up = glm::normalize(glm::cross(right, front));
}
void FreeCamera::update_projection() {
_projection = glm::perspective(glm::radians(75.f), _aspectRatio, 0.1f, 100.0f);
}
void FreeCamera::update_view() {
_view = glm::lookAt(_position, _position + front, up);
}

61
src/FreeCamera.h Normal file
View File

@@ -0,0 +1,61 @@
//
// Created by slinky on 5/3/26.
//
#ifndef B_ENGINE_CAMERA_H
#define B_ENGINE_CAMERA_H
#include <glm/glm.hpp>
constexpr float MAX_PITCH = 89.f;
constexpr float MIN_PITCH = -89.f;
constexpr glm::vec3 WORLD_UP = {0.0f, 1.0f, 0.0f};
enum CAMERA_MOVEMENT {
FORWARD,
BACKWARD,
LEFT,
RIGHT,
UP,
DOWN,
};
class FreeCamera {
public:
FreeCamera() = default;
FreeCamera(const glm::vec3 &pos, float pitch, float yaw, float aspectRatio);
[[nodiscard]] const glm::mat4& projection() const;
[[nodiscard]] const glm::mat4& view() const;
[[nodiscard]] const glm::vec3& position() const;
void update_aspect_ratio(float aspectRatio);
void rotate_yaw(float offset);
void rotate_pitch(float offset);
void move(CAMERA_MOVEMENT direction, float offset);
private:
void update_camera_vectors();
void update_projection();
void update_view();
glm::vec3 _position{};
glm::vec3 front{};
glm::vec3 up{};
glm::vec3 right{};
glm::mat4 _view{};
glm::mat4 _projection{};
float _yaw = 0;
float _pitch = 0;
float _aspectRatio = 0;
float _sensitivity = 0.1f;
};
#endif //B_ENGINE_CAMERA_H

118
src/InputManager.cpp Normal file
View File

@@ -0,0 +1,118 @@
//
// Created by slinky on 5/3/26.
//
#include "InputManager.h"
bool InputManager::mouseInit = false;
float InputManager::lastMousePositionX = 0;
float InputManager::lastMousePositionY = 0;
std::unordered_map<std::string, std::unique_ptr<InputAction>> InputManager::registeredActions = {};
static bool currentKeyStatesData[512] = {};
bool* InputManager::currentKeyStates = currentKeyStatesData;
static bool previousKeyStatesData[512] = {};
bool* InputManager::previousKeyStates = previousKeyStatesData;
std::vector<MouseDeltaCallback> InputManager::mouse_listeners = {};
void InputManager::mouse_pos_callback(GLFWwindow *window, double x, double y) {
const auto xpos = static_cast<float>(x);
const auto ypos = static_cast<float>(y);
if (!mouseInit) {
lastMousePositionX = xpos;
lastMousePositionY = ypos;
mouseInit = true;
}
float xOff = xpos - lastMousePositionX;
float yOff = lastMousePositionY - ypos;
lastMousePositionX = xpos;
lastMousePositionY = ypos;
for (const auto& cb : mouse_listeners) {
cb(xOff, yOff);
}
}
void InputManager::add_mouse_listener(MouseDeltaCallback callback) {
mouse_listeners.push_back(callback);
}
void InputManager::key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
if (action == GLFW_REPEAT) return;
set_key_state(key, action == GLFW_PRESS);
}
void InputManager::generate_input_action(std::string_view actionName, std::initializer_list<InputRequirement> requirements) {
if (registeredActions.contains(actionName.data())) {
return;
}
InputAction action;
for (const auto& requirement : requirements) {
action.requirements.push_back(requirement);
}
const std::string key = actionName.data();
registeredActions[key] = std::make_unique<InputAction>(action);
}
bool InputManager::check_action_performed(std::string_view actionName) {
if (!registeredActions.contains(actionName.data())) {
return false;
}
InputAction* action = registeredActions[actionName.data()].get();
if (!action) {
return false;
}
for (const auto&[key, state] : action->requirements) {
switch (state) {
case KEY_DOWN:
if (!key_down(key))
return false;
break;
case KEY_HELD:
if (!key_held(key))
return false;
break;
case KEY_PRESSED:
if (!key_pressed(key))
return false;
break;
case KEY_RELEASED:
if (!key_released(key))
return false;
break;
}
}
return true;
}
bool InputManager::key_down(int key) {
return currentKeyStatesData[key];
}
bool InputManager::key_up(int key) {
return !currentKeyStatesData[key];
}
bool InputManager::key_pressed(int key) {
return !previousKeyStatesData[key] && currentKeyStatesData[key];
}
bool InputManager::key_released(int key) {
return previousKeyStatesData[key] && !currentKeyStatesData[key];
}
bool InputManager::key_held(int key) {
return previousKeyStatesData[key] && currentKeyStatesData[key];
}
void InputManager::set_key_state(int key, bool state) {
previousKeyStatesData[key] = currentKeyStatesData[key];
currentKeyStatesData[key] = state;
}

61
src/InputManager.h Normal file
View File

@@ -0,0 +1,61 @@
//
// Created by slinky on 5/3/26.
//
#ifndef B_ENGINE_INPUTMANAGER_H
#define B_ENGINE_INPUTMANAGER_H
#include <functional>
#include <string>
#include <initializer_list>
#include <memory>
#include "GLFW/glfw3.h"
using MouseDeltaCallback = std::function<void(float, float)>;
enum KeyState {
KEY_PRESSED,
KEY_HELD,
KEY_DOWN,
KEY_RELEASED,
};
struct InputRequirement {
int key;
KeyState state;
};
struct InputAction {
std::vector<InputRequirement> requirements{};
};
class InputManager {
public:
static bool mouseInit;
static float lastMousePositionX;
static float lastMousePositionY;
static bool* currentKeyStates;
static bool* previousKeyStates;
static std::unordered_map<std::string, std::unique_ptr<InputAction>> registeredActions;
static void mouse_pos_callback(GLFWwindow* window, double x, double y);
static void add_mouse_listener(MouseDeltaCallback callback);
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
static void generate_input_action(std::string_view actionName, std::initializer_list<InputRequirement> requirements);
static bool check_action_performed(std::string_view actionName);
static bool key_pressed(int key);
static bool key_held(int key);
static bool key_released(int key);
static bool key_down(int key);
static bool key_up(int key);
private:
static std::vector<MouseDeltaCallback> mouse_listeners;
static void set_key_state(int key, bool state);
};
#endif //B_ENGINE_INPUTMANAGER_H

View File

@@ -1,5 +0,0 @@
//
// Created by lbmas on 4/28/2026.
//
#include "Material.h"

View File

@@ -8,7 +8,6 @@ Mesh::Mesh(const std::vector<float> &positions, const std::vector<float> &uvs, c
ebo = 0;
numIndices = indices.size();
// positions
GLuint positionVbo = 0;
glGenBuffers(1, &positionVbo);

View File

@@ -18,9 +18,10 @@ public:
const std::vector<float>& normals,
const std::vector<unsigned int>& indices);
GLuint vao;
GLuint ebo;
unsigned int numIndices;
GLuint vao = 0;
GLuint ebo = 0;
unsigned int numIndices = 0;
unsigned int materialId = 0;
};
#endif //B_ENGINE_MESH_H

View File

@@ -7,15 +7,13 @@
#include <string_view>
#include <vector>
#include <filesystem>
#include "Mesh.h"
#include "Material.h"
class Model
struct Model
{
public:
static std::shared_ptr<Model> load_from_file(std::string_view filename);
std::vector<std::shared_ptr<Mesh>> meshes;
std::vector<std::shared_ptr<Material>> materials;
};

View File

@@ -4,7 +4,7 @@
#include <spdlog/spdlog.h>
#include "ModelLoader.h"
#include "ModelManager.h"
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
@@ -12,15 +12,17 @@
#include <cstring>
#include "TextureLoader.h"
#include "TextureManager.h"
#include "glm/ext/matrix_transform.hpp"
std::unordered_map<std::string, std::shared_ptr<Model>> ModelLoader::models;
std::unordered_map<std::string, std::shared_ptr<Model>> ModelManager::models;
void process_ai_mesh(aiMesh* aiMesh, const aiScene* scene, glm::mat4 transform, Mesh& mesh);
void process_ai_node(aiNode* node, const aiScene* scene, glm::mat4 transform, Model* model);
void process_ai_material(aiMaterial* aiMat, const aiScene* scene, Material& mat);
void process_ai_material(aiMaterial* aiMat, const aiScene* scene, Material& mat, const std::filesystem::path& modelDirectory);
void process_ai_material_diffuse(const aiMaterial *aiMat, Material& mat, const std::filesystem::path& modelDirectory);
void process_ai_material_specular(const aiMaterial *aiMat, Material& mat, const std::filesystem::path& modelDirectory);
auto zUpMatrix = glm::mat4(
1.0f, 0.0f, 0.0f, 0.0f, // Column 0
@@ -29,7 +31,7 @@ auto zUpMatrix = glm::mat4(
0.0f, 0.0f, 0.0f, 1.0f // Column 3
);
std::shared_ptr<Model> ModelLoader::load_from_file(std::string_view _path, bool zUp)
std::shared_ptr<Model> ModelManager::load_from_file(std::string_view _path, bool zUp)
{
Assimp::Importer importer;
@@ -46,6 +48,16 @@ std::shared_ptr<Model> ModelLoader::load_from_file(std::string_view _path, bool
auto model = std::make_shared<Model>();
std::filesystem::path modelPath = {_path};
model->materials.resize(scene->mNumMaterials);
for (unsigned int i = 0; i < scene->mNumMaterials; i++) {
aiMaterial* aiMat = scene->mMaterials[i];
Material mat{};
process_ai_material(aiMat, scene, mat, modelPath.parent_path());
model->materials[i] = std::make_shared<Material>(mat);
}
auto transform = glm::identity<glm::mat4>();
if (zUp) {
transform = zUpMatrix;
@@ -58,20 +70,16 @@ std::shared_ptr<Model> ModelLoader::load_from_file(std::string_view _path, bool
void process_ai_node(aiNode* node, const aiScene* scene, glm::mat4 transform, Model* model)
{
auto m = node->mTransformation;
transform = transform * glm::mat4(m.a1, m.a2, m.a3, m.a4, m.b1, m.b2, m.b3, m.b4, m.c1, m.c2, m.c3, m.c4, m.d1, m.d2, m.d3, m.d4);
const auto t = node->mTransformation;
transform = transform * glm::mat4(t.a1, t.a2, t.a3, t.a4, t.b1, t.b2, t.b3, t.b4, t.c1, t.c2, t.c3, t.c4, t.d1, t.d2, t.d3, t.d4);
for (unsigned int i = 0; i < node->mNumMeshes; i++)
{
aiMesh* aiMesh = scene->mMeshes[node->mMeshes[i]];
Mesh mesh{};
mesh.materialId = aiMesh->mMaterialIndex;
process_ai_mesh(aiMesh, scene, transform, mesh);
model->meshes.push_back(std::make_shared<Mesh>(mesh));
aiMaterial* aiMat = scene->mMaterials[aiMesh->mMaterialIndex];
Material mat{};
process_ai_material(aiMat, scene, mat);
model->materials.push_back(std::make_shared<Material>(mat));
}
for (unsigned int i = 0; i < node->mNumChildren; i++)
@@ -130,7 +138,7 @@ void process_ai_mesh(aiMesh* aiMesh, const aiScene* scene, glm::mat4 transform,
mesh = {positions, uvs, normals, indices};
}
void process_ai_material(aiMaterial* aiMat, const aiScene* scene, Material& mat) {
void process_ai_material(aiMaterial* aiMat, const aiScene* scene, Material& mat, const std::filesystem::path& modelDirectory) {
aiString matName;
aiReturn ret = aiMat->Get(AI_MATKEY_NAME, matName);
if (ret != AI_SUCCESS) {
@@ -167,14 +175,8 @@ void process_ai_material(aiMaterial* aiMat, const aiScene* scene, Material& mat)
}
// now grab the textures, first diffuse
for (unsigned int i = 0; i < aiMat->GetTextureCount(aiTextureType_DIFFUSE); i++) {
aiString textureName;
aiMat->GetTexture(aiTextureType_DIFFUSE, i, &textureName);
if (!TextureLoader::textures.contains(textureName.C_Str())) {
TextureLoader::textures[textureName.C_Str()] = TextureLoader::load_from_file(textureName.C_Str());
}
}
process_ai_material_diffuse(aiMat, mat, modelDirectory);
process_ai_material_specular(aiMat, mat, modelDirectory);
mat.name = matName.C_Str();
mat.phong.ambient = glm::vec3{ambientColor.r, ambientColor.g, ambientColor.b};
@@ -182,3 +184,69 @@ void process_ai_material(aiMaterial* aiMat, const aiScene* scene, Material& mat)
mat.phong.specular = glm::vec3{specularColor.r, specularColor.g, specularColor.b};
mat.phong.shininess = shininess;
}
void process_ai_material_diffuse(const aiMaterial *aiMat, Material& mat, const std::filesystem::path& modelDirectory) {
aiString aiTextureName;
aiMat->GetTexture(aiTextureType_DIFFUSE, 0, &aiTextureName);
std::string textureName = aiTextureName.C_Str();
if (textureName.empty()) {
mat.diffuse = TextureManager::textures["default_diffuse"];
return;
}
const auto textureFilename = std::filesystem::path(textureName).filename();
const auto texturePath = modelDirectory / textureFilename;
const std::string finalTextureName = textureFilename.string();
if (TextureManager::textures.contains(finalTextureName)) {
mat.diffuse = TextureManager::textures[finalTextureName];
return;
}
if (std::filesystem::exists(texturePath)) {
const auto texture = TextureManager::load_from_file(texturePath.string());
if (!texture) {
mat.diffuse = TextureManager::textures["default_diffuse"];
return;
}
TextureManager::textures[finalTextureName] = texture;
mat.diffuse = texture;
} else {
mat.diffuse = TextureManager::textures["default_diffuse"];
}
}
void process_ai_material_specular(const aiMaterial *aiMat, Material& mat, const std::filesystem::path& modelDirectory) {
aiString aiTextureName;
aiMat->GetTexture(aiTextureType_SPECULAR, 0, &aiTextureName);
std::string textureName = aiTextureName.C_Str();
if (textureName.empty()) {
mat.specular = TextureManager::textures["default_specular"];
return;
}
const auto textureFilename = std::filesystem::path(textureName).filename();
const auto texturePath = modelDirectory / textureFilename;
const std::string finalTextureName = textureFilename.string();
if (TextureManager::textures.contains(finalTextureName)) {
mat.specular = TextureManager::textures[finalTextureName];
return;
}
if (std::filesystem::exists(texturePath)) {
const auto texture = TextureManager::load_from_file(texturePath.string());
if (!texture) {
mat.specular = TextureManager::textures["default_specular"];
return;
}
TextureManager::textures[finalTextureName] = texture;
mat.specular = texture;
} else {
mat.specular = TextureManager::textures["default_specular"];
}
}

View File

@@ -10,7 +10,7 @@
#include "Model.h"
class ModelLoader
class ModelManager
{
public:
static std::unordered_map<std::string, std::shared_ptr<Model>> models;

View File

@@ -16,6 +16,9 @@ public:
Texture(unsigned int id, std::string_view filePath, int width, int height, int channels)
: id(id), filePath(filePath), width(width), height(height), channels(channels) {}
Texture(const Texture&) = delete;
Texture& operator=(const Texture&) = delete;
~Texture() { glDeleteTextures(1, &id); }
void bind() const {glBindTexture(GL_TEXTURE_2D, id);}

View File

@@ -2,7 +2,7 @@
// Created by slinky on 5/1/26.
//
#include "TextureLoader.h"
#include "TextureManager.h"
#include "stb_image.h"
@@ -10,9 +10,9 @@
#include "glad/gl.h"
std::unordered_map<std::string, std::shared_ptr<Texture>> TextureLoader::textures;
std::unordered_map<std::string, std::shared_ptr<Texture>> TextureManager::textures;
std::shared_ptr<Texture> TextureLoader::load_from_file(std::string_view filePath)
std::shared_ptr<Texture> TextureManager::load_from_file(std::string_view filePath)
{
int width, height, channels;
unsigned char *data = stbi_load(filePath.data(), &width, &height, &channels, 0);
@@ -41,7 +41,7 @@ std::shared_ptr<Texture> TextureLoader::load_from_file(std::string_view filePath
return std::make_shared<Texture>(textureID, filePath, width, height, channels);
}
std::shared_ptr<Texture> TextureLoader::load_from_data(unsigned char* data, int width, int height, int channels)
std::shared_ptr<Texture> TextureManager::load_from_data(unsigned char* data, int width, int height, int channels)
{
unsigned int textureID = 0;
glGenTextures(1, &textureID);

View File

@@ -2,15 +2,15 @@
// Created by slinky on 5/1/26.
//
#ifndef B_ENGINE_TEXTURELOADER_H
#define B_ENGINE_TEXTURELOADER_H
#ifndef B_ENGINE_TEXTUREMANAGER_H
#define B_ENGINE_TEXTUREMANAGER_H
#include <memory>
#include <unordered_map>
#include "Texture.h"
class TextureLoader {
class TextureManager {
public:
static std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
@@ -18,4 +18,4 @@ public:
static std::shared_ptr<Texture> load_from_data(unsigned char* data, int width, int height, int channels);
};
#endif //B_ENGINE_TEXTURELOADER_H
#endif //B_ENGINE_TEXTUREMANAGER_H

View File

@@ -8,14 +8,16 @@
#include "glm/ext/matrix_transform.hpp"
#define STB_IMAGE_IMPLEMENTATION
#include "FreeCamera.h"
#include "InputManager.h"
#include "stb_image.h"
#include "Model.h"
#include "ModelLoader.h"
#include "ModelManager.h"
#include "ShaderManager.h"
#include "ShaderProgram.h"
#include "Texture.h"
#include "TextureLoader.h"
#include "TextureManager.h"
GLFWwindow* gWindow = nullptr;
int gWindowWidth = 800;
@@ -24,9 +26,11 @@ int gWindowHeight = 600;
int glVersionMajor = 0;
int glVersionMinor = 0;
auto cameraPosition = glm::vec3{0, 0, 0};
auto view = glm::mat4{0};
auto projection = glm::mat4{0};
float gAspectRatio = 0.f;
float gMouseSensitivity = 0.1f;
FreeCamera gCamera {};
void glfw_error_callback(int error, const char* description);
void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
@@ -36,10 +40,16 @@ void init_glfw();
void create_main_window();
void load_gl();
void init_camera();
void load_default_textures();
void load_shaders();
void loop();
std::shared_ptr<Model> load_model(std::string_view _path);
auto xAxis = glm::vec3{1.f, 0.f, 0.f};
auto yAxis = glm::vec3{0.f, 1.f, 0.f};
auto zAxis = glm::vec3{0.f, 0.f, 1.f};
int main() {
spdlog::info("b_engine start");
@@ -49,30 +59,33 @@ int main() {
load_gl();
stbi_set_flip_vertically_on_load(true);
init_camera();
// create a default texture
unsigned char defaultDiffuseData[4] = {static_cast<unsigned char>(255), 255, 255, 255};
TextureLoader::textures["default_diffuse"] = TextureLoader::load_from_data(reinterpret_cast<unsigned char*>(&defaultDiffuseData), 1, 1, 4);
stbi_set_flip_vertically_on_load(true);
// create a default specular map
unsigned char defaultSpecularData[4] = {static_cast<unsigned char>(128), 128, 128, 255};
TextureLoader::textures["default_specular"] = TextureLoader::load_from_data(reinterpret_cast<unsigned char*>(&defaultSpecularData), 1, 1, 4);
load_default_textures();
ShaderManager::shaders["phong_shader"] = ShaderManager::load("./resources/standard.vert", "./resources/standard.frag");
if (ShaderManager::shaders["phong_shader"] == nullptr)
{
std::exit(1);
}
load_shaders();
/*ModelLoader::models["cube"] = ModelLoader::load_from_file("./resources/cube.obj", true);*/
ModelLoader::models["backpack"] = ModelLoader::load_from_file("./resources/backpack/backpack.obj");
/*ModelLoader::models["male"] = ModelLoader::load_from_file("./resources/male.obj");*/
InputManager::generate_input_action("move_forward", {{GLFW_KEY_W, KEY_DOWN}});
InputManager::generate_input_action("move_backward", {{GLFW_KEY_S, KEY_DOWN}});
InputManager::generate_input_action("move_right", {{GLFW_KEY_D, KEY_DOWN}});
InputManager::generate_input_action("move_left", {{GLFW_KEY_A, KEY_DOWN}});
InputManager::generate_input_action("exit_application", {{GLFW_KEY_ESCAPE, KEY_DOWN}});
spdlog::info("loading models");
ModelManager::models["cube"] = ModelManager::load_from_file("./resources/cube.obj");
ModelManager::models["backpack"] = ModelManager::load_from_file("./resources/backpack/backpack.obj");
ModelManager::models["male"] = ModelManager::load_from_file("./resources/male.obj");
spdlog::info("done");
loop();
ShaderManager::shaders.clear();
TextureManager::textures.clear();
ModelManager::models.clear();
glfwDestroyWindow(gWindow);
glfwTerminate();
@@ -83,14 +96,10 @@ void glfw_error_callback(int error, const char* description) {
spdlog::error("glfw error: {}", description);
}
void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
glfwSetWindowShouldClose(window, GL_TRUE);
}
}
void glfw_framebuffer_size_callback(GLFWwindow *window, int width, int height) {
glViewport(0, 0, width, height);
gAspectRatio = (float)width / (float)height;
gCamera.update_aspect_ratio(gAspectRatio);
}
void init_glfw()
@@ -103,13 +112,14 @@ void init_glfw()
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_SAMPLES, 4);
glfwSetErrorCallback(glfw_error_callback);
}
void create_main_window()
{
gWindow = glfwCreateWindow(gWindowWidth, gWindowHeight, "b_engine v0.0.1", nullptr, nullptr);
gWindow = glfwCreateWindow(gWindowWidth, gWindowHeight, "b_engine v0.0.2", nullptr, nullptr);
if (!gWindow) {
spdlog::error("failed to create glfw window");
std::exit(1);
@@ -117,8 +127,12 @@ void create_main_window()
glfwMakeContextCurrent(gWindow);
glfwSetKeyCallback(gWindow, glfw_key_callback);
glfwSetKeyCallback(gWindow, InputManager::key_callback);
glfwSetCursorPosCallback(gWindow, InputManager::mouse_pos_callback);
glfwSetFramebufferSizeCallback(gWindow, glfw_framebuffer_size_callback);
glfwSetInputMode(gWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
}
void load_gl()
@@ -132,63 +146,131 @@ void load_gl()
{
int fbWidth, fbHeight;
glfwGetFramebufferSize(gWindow, &fbWidth, &fbHeight);
glViewport(0, 0, fbWidth, fbHeight);
gAspectRatio = (float)fbWidth / (float)fbHeight;
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_MULTISAMPLE);
}
void init_camera()
{
cameraPosition = glm::vec3{10, 0, 10};
auto cameraTarget = glm::vec3{0, 0, 0};
void init_camera() {
gCamera = {{5.f, 5.f, 5.f}, 0, 0, gAspectRatio};
gCamera.rotate_yaw(-135.f);
gCamera.rotate_pitch(-35.f);
view = glm::lookAt(cameraPosition, cameraTarget, glm::vec3(0.0f, 1.0f, 0.0f));
InputManager::add_mouse_listener([](float xoff, float yoff) {
gCamera.rotate_yaw(xoff * gMouseSensitivity);
gCamera.rotate_pitch(yoff * gMouseSensitivity);
});
}
float aspectRatio = static_cast<float>(gWindowWidth) / static_cast<float>(gWindowHeight);
projection = glm::perspective(glm::radians(75.f), aspectRatio, 0.1f, 100.0f);
void load_default_textures() {
spdlog::info("creating default textures");
unsigned char defaultDiffuseData[4] = {static_cast<unsigned char>(255), 255, 255, 255};
TextureManager::textures["default_diffuse"] = TextureManager::load_from_data(reinterpret_cast<unsigned char*>(&defaultDiffuseData), 1, 1, 4);
unsigned char defaultSpecularData[4] = {static_cast<unsigned char>(64), 64, 64, 255};
TextureManager::textures["default_specular"] = TextureManager::load_from_data(reinterpret_cast<unsigned char*>(&defaultSpecularData), 1, 1, 4);
}
void load_shaders() {
spdlog::info("compiling shaders");
ShaderManager::shaders["phong_shader"] = ShaderManager::load("./resources/standard.vert", "./resources/standard.frag");
if (ShaderManager::shaders["phong_shader"] == nullptr)
{
spdlog::error("failed to compile phong shader");
std::exit(1);
}
}
void loop() {
Texture* defaultDiffuse = TextureLoader::textures["default_diffuse"].get();
Texture* defaultSpecular = TextureLoader::textures["default_specular"].get();
std::shared_ptr<Texture> defaultDiffuse = TextureManager::textures["default_diffuse"];
std::shared_ptr<Texture> defaultSpecular = TextureManager::textures["default_specular"];
while (!glfwWindowShouldClose(gWindow)) {
auto model = glm::identity<glm::mat4>();
// model = glm::translate(model, glm::vec3(0, -5.f, 0));
// model = glm::scale(model, glm::vec3(.5f, .5f, .5f));
glfwPollEvents();
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ShaderProgram* shader = ShaderManager::shaders["phong_shader"].get();
const Model* cube = ModelLoader::models["cube"].get();
const Model* backpack = ModelLoader::models["backpack"].get();
const Model* male = ModelLoader::models["male"].get();
const auto cube = ModelManager::models["cube"];
const auto backpack = ModelManager::models["backpack"];
const auto male = ModelManager::models["male"];
if (shader) {
Material* mat = backpack->materials[0].get();
const auto& activeModel = backpack;
while (!glfwWindowShouldClose(gWindow)) {
glfwPollEvents();
// check inputs
if (InputManager::check_action_performed("exit_application")) {
glfwSetWindowShouldClose(gWindow, true);
continue;
}
if (InputManager::check_action_performed("move_forward")) {
gCamera.move(CAMERA_MOVEMENT::FORWARD, .05);
}
if (InputManager::check_action_performed("move_backward")) {
gCamera.move(CAMERA_MOVEMENT::BACKWARD, .05);
}
if (InputManager::check_action_performed("move_left")) {
gCamera.move(CAMERA_MOVEMENT::LEFT, .05);
}
if (InputManager::check_action_performed("move_right")) {
gCamera.move(CAMERA_MOVEMENT::RIGHT, .05);
}
// gl frame prep
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// draw the stuff
if (shader && activeModel) {
shader->bind();
shader->setMat4("projection", projection);
shader->setMat4("view", view);
shader->setMat4("projection", gCamera.projection());
shader->setMat4("view", gCamera.view());
shader->setMat4("model", model);
shader->setVec3("viewPosition", cameraPosition);
shader->setVec3("viewPosition", gCamera.position());
shader->setVec3("phongAmbient", glm::vec3(0.3f, 0.3f, 0.3f));
shader->setVec3("phongDiffuse", glm::vec3(0.8f, 0.8f, 0.8f));
shader->setVec3("phongSpecular", glm::vec3(1.0f, 1.0f, 1.0f));
shader->setFloat("phongShininess", 32.0f);
shader->setVec3("lightDirection", glm::vec3(-.5f, -.5f, -.5f));
shader->setVec3("lightPosition", glm::vec3{-2.f, 0, 2.0f});
shader->setVec3("lightDirection", glm::vec3(1, -1, -1));
shader->setVec3("lightAmbient", glm::vec3(0.3f, 0.3f, 0.3f));
shader->setVec3("lightDiffuse", glm::vec3(1.0f, 1.0f, 1.0f));
shader->setVec3("lightDiffuse", glm::vec3(0.5f, 0.5f, 0.5f));
shader->setVec3("lightSpecular", glm::vec3(1.0f, 1.0f, 1.0f));
for (const auto& mesh: backpack->meshes) {
for (const auto& mesh: activeModel->meshes) {
unsigned int materialId = mesh->materialId;
if (materialId >= activeModel->materials.size()) {
materialId = 0;
}
const std::shared_ptr<Material> mat = activeModel->materials[1];
shader->setVec3("phongAmbient", mat->phong.ambient);
shader->setVec3("phongDiffuse", mat->phong.diffuse);
shader->setVec3("phongSpecular", mat->phong.specular);
shader->setFloat("phongShininess", mat->phong.shininess);
glActiveTexture(GL_TEXTURE0);
auto diffuse = mat->diffuse;
if (!diffuse) {
diffuse = defaultDiffuse;
}
diffuse->bind();
shader->setInt("diffuseMap", 0);
glActiveTexture(GL_TEXTURE1);
auto specular = mat->specular;
if (!specular) {
specular = defaultSpecular;
}
specular->bind();
shader->setInt("specularMap", 1);
glBindVertexArray(mesh.get()->vao);
glDrawElements(GL_TRIANGLES, mesh->numIndices, GL_UNSIGNED_INT, 0);
}
@@ -197,6 +279,7 @@ void loop() {
ShaderProgram::unbind();
}
// gl end frame stuff
glfwSwapBuffers(gWindow);
}
}