started on assimp importing and texture stuff
This commit is contained in:
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -4,3 +4,9 @@
|
|||||||
[submodule "_ThirdParty/glfw"]
|
[submodule "_ThirdParty/glfw"]
|
||||||
path = _ThirdParty/glfw
|
path = _ThirdParty/glfw
|
||||||
url = https://github.com/glfw/glfw.git
|
url = https://github.com/glfw/glfw.git
|
||||||
|
[submodule "_ThirdParty/assimp"]
|
||||||
|
path = _ThirdParty/assimp
|
||||||
|
url = https://github.com/assimp/assimp.git
|
||||||
|
[submodule "glm"]
|
||||||
|
path = glm
|
||||||
|
url = https://github.com/g-truc/glm.git
|
||||||
|
|||||||
@@ -8,14 +8,31 @@ set (GLAD_INCLUDE_DIR _ThirdParty/glad/include)
|
|||||||
|
|
||||||
include_directories(${GLAD_INCLUDE_DIR})
|
include_directories(${GLAD_INCLUDE_DIR})
|
||||||
|
|
||||||
set (GLFW_BUILD_EXAMPLES FALSE)
|
set (GLFW_BUILD_EXAMPLES OFF)
|
||||||
set (GLFW_BUILD_TESTS FALSE)
|
set (GLFW_BUILD_TESTS OFF)
|
||||||
set (GLFW_BUILD_DOCS FALSE)
|
set (GLFW_BUILD_DOCS OFF)
|
||||||
add_subdirectory(_ThirdParty/glfw)
|
add_subdirectory(_ThirdParty/glfw)
|
||||||
|
|
||||||
add_subdirectory(_ThirdParty/spdlog)
|
add_subdirectory(_ThirdParty/spdlog)
|
||||||
|
|
||||||
|
set (ASSIMP_INSTALL OFF)
|
||||||
|
add_subdirectory(_ThirdParty/assimp)
|
||||||
|
|
||||||
|
add_subdirectory(glm)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} src/main.cpp ${GLAD_SOURCE_DIR}/gl.c
|
add_executable(${PROJECT_NAME} src/main.cpp ${GLAD_SOURCE_DIR}/gl.c
|
||||||
src/Mesh.cpp
|
src/Mesh.cpp
|
||||||
src/Mesh.h)
|
src/Mesh.h
|
||||||
target_link_libraries(${PROJECT_NAME} glfw spdlog)
|
src/Model.cpp
|
||||||
|
src/Model.h
|
||||||
|
src/Material.cpp
|
||||||
|
src/Material.h
|
||||||
|
src/ModelLoader.cpp
|
||||||
|
src/ModelLoader.h
|
||||||
|
src/ShaderProgram.cpp
|
||||||
|
src/ShaderProgram.h
|
||||||
|
src/Texture.cpp
|
||||||
|
src/Texture.h)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME} glfw spdlog assimp glm)
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE ${ASSIMP_INCLUDE_INSTALL_DIR})
|
||||||
|
|||||||
1
_ThirdParty/assimp
vendored
Submodule
1
_ThirdParty/assimp
vendored
Submodule
Submodule _ThirdParty/assimp added at 86ae4876fb
1
glm
Submodule
1
glm
Submodule
Submodule glm added at 6f14f4792a
42
resources/standard.frag
Normal file
42
resources/standard.frag
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#version 400
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
in vec3 Position;
|
||||||
|
in vec2 TexCoord;
|
||||||
|
in vec3 Normal;
|
||||||
|
|
||||||
|
uniform vec3 viewPosition;
|
||||||
|
|
||||||
|
uniform sampler2D diffuse1;
|
||||||
|
uniform sampler2D specular1;
|
||||||
|
|
||||||
|
uniform vec3 phongAmbient;
|
||||||
|
uniform vec3 phongDiffuse;
|
||||||
|
uniform vec3 phongSpecular;
|
||||||
|
uniform float phongShininess;
|
||||||
|
|
||||||
|
uniform vec3 lightPos;
|
||||||
|
uniform vec3 lightAmbient;
|
||||||
|
uniform vec3 lightDiffuse;
|
||||||
|
uniform vec3 lightSpecular;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 diffColor = texture(diffuse1, TexCoord).rgb;
|
||||||
|
vec3 specColor = texture(specular1, TexCoord).rgb;
|
||||||
|
|
||||||
|
vec3 ambient = (phongAmbient * diffColor) * lightAmbient;
|
||||||
|
|
||||||
|
vec3 norm = normalize(Normal);
|
||||||
|
vec3 lightDir = normalize(lightPos - Position);
|
||||||
|
float diffImpact = max(dot(norm, lightDir), 0.0);
|
||||||
|
vec3 diffuse = (phongDiffuse * diffImpact * diffColor) * lightDiffuse;
|
||||||
|
|
||||||
|
vec3 viewDir = normalize(viewPosition - Position);
|
||||||
|
vec3 halfwayDir = normalize(lightDir + viewDir);
|
||||||
|
float specImpact = pow(max(dot(norm, halfwayDir), 0.0), phongShininess);
|
||||||
|
vec3 specular = (phongSpecular * specImpact * specColor) * lightSpecular;
|
||||||
|
|
||||||
|
vec3 result = ambient + diffuse + specular;
|
||||||
|
FragColor = vec4(result, 1.0);
|
||||||
|
}
|
||||||
22
resources/standard.vert
Normal file
22
resources/standard.vert
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#version 400
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 position;
|
||||||
|
layout (location = 1) in vec2 uv;
|
||||||
|
layout (location = 2) in vec3 normal;
|
||||||
|
|
||||||
|
out vec3 Position;
|
||||||
|
out vec2 TexCoord;
|
||||||
|
out vec3 Normal;
|
||||||
|
|
||||||
|
uniform mat4 model;
|
||||||
|
uniform mat4 view;
|
||||||
|
uniform mat4 projection;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Position = vec3(model * vec4(position, 1.0));
|
||||||
|
Normal = mat3(transpose(inverse(model))) * normal;
|
||||||
|
|
||||||
|
TexCoord = uv;
|
||||||
|
|
||||||
|
gl_Position = projection * view * vec4(Position, 1.0);
|
||||||
|
}
|
||||||
5
src/Material.cpp
Normal file
5
src/Material.cpp
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Created by lbmas on 4/28/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Material.h"
|
||||||
30
src/Material.h
Normal file
30
src/Material.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// Created by lbmas on 4/28/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef B_ENGINE_MATERIAL_H
|
||||||
|
#define B_ENGINE_MATERIAL_H
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include "Texture.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
alignas(16) glm::vec3 ambient;
|
||||||
|
alignas(16) glm::vec3 diffuse;
|
||||||
|
alignas(16) glm::vec3 specular;
|
||||||
|
float shininess;
|
||||||
|
} PhongProperties;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Texture* diffuse;
|
||||||
|
Texture* specular;
|
||||||
|
Texture* normal;
|
||||||
|
|
||||||
|
PhongProperties phong;
|
||||||
|
} Material;
|
||||||
|
|
||||||
|
|
||||||
|
#endif //B_ENGINE_MATERIAL_H
|
||||||
23
src/Mesh.cpp
23
src/Mesh.cpp
@@ -3,8 +3,9 @@
|
|||||||
//
|
//
|
||||||
#include "Mesh.h"
|
#include "Mesh.h"
|
||||||
|
|
||||||
Mesh::Mesh(const std::vector<float> &positions, const std::vector<float> &uvs, const std::vector<float> &normals, const std::vector<int> &indices) {
|
Mesh::Mesh(const std::vector<float> &positions, const std::vector<float> &uvs, const std::vector<float> &normals, const std::vector<unsigned int> &indices) {
|
||||||
vao = 0;
|
vao = 0;
|
||||||
|
ebo = 0;
|
||||||
glGenVertexArrays(1, &vao);
|
glGenVertexArrays(1, &vao);
|
||||||
glBindVertexArray(vao);
|
glBindVertexArray(vao);
|
||||||
|
|
||||||
@@ -25,4 +26,24 @@ Mesh::Mesh(const std::vector<float> &positions, const std::vector<float> &uvs, c
|
|||||||
glGenBuffers(1, &normalVbo);
|
glGenBuffers(1, &normalVbo);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, normalVbo);
|
glBindBuffer(GL_ARRAY_BUFFER, normalVbo);
|
||||||
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(float), normals.data(), GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(float), normals.data(), GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glGenVertexArrays(1, &vao);
|
||||||
|
glBindVertexArray(vao);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, positionVbo);
|
||||||
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, uvVbo);
|
||||||
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(2);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, normalVbo);
|
||||||
|
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||||
|
|
||||||
|
glBindVertexArray(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,10 @@ public:
|
|||||||
Mesh(const std::vector<float>& positions,
|
Mesh(const std::vector<float>& positions,
|
||||||
const std::vector<float>& uvs,
|
const std::vector<float>& uvs,
|
||||||
const std::vector<float>& normals,
|
const std::vector<float>& normals,
|
||||||
const std::vector<float>& indices);
|
const std::vector<unsigned int>& indices);
|
||||||
|
|
||||||
void bind();
|
|
||||||
private:
|
|
||||||
GLuint vao;
|
GLuint vao;
|
||||||
|
GLuint ebo;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //B_ENGINE_MESH_H
|
#endif //B_ENGINE_MESH_H
|
||||||
5
src/Model.cpp
Normal file
5
src/Model.cpp
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Created by lbmas on 4/28/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Model.h"
|
||||||
22
src/Model.h
Normal file
22
src/Model.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// Created by lbmas on 4/28/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef B_ENGINE_MODEL_H
|
||||||
|
#define B_ENGINE_MODEL_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Mesh.h"
|
||||||
|
#include "Material.h"
|
||||||
|
|
||||||
|
class Model
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int numMeshes;
|
||||||
|
std::vector<Mesh> meshes;
|
||||||
|
std::vector<Material> materials;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //B_ENGINE_MODEL_H
|
||||||
94
src/ModelLoader.cpp
Normal file
94
src/ModelLoader.cpp
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
//
|
||||||
|
// Created by lbmas on 4/29/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include "ModelLoader.h"
|
||||||
|
|
||||||
|
#include <assimp/Importer.hpp>
|
||||||
|
#include <assimp/postprocess.h>
|
||||||
|
#include <assimp/scene.h>
|
||||||
|
|
||||||
|
void process_ai_node(aiNode* node, const aiScene* scene, Model* model);
|
||||||
|
Mesh process_ai_mesh(aiMesh* aiMesh, const aiScene* scene, Model* model);
|
||||||
|
|
||||||
|
std::shared_ptr<Model> ModelLoader::load_from_file(std::string_view _path)
|
||||||
|
{
|
||||||
|
Assimp::Importer importer;
|
||||||
|
|
||||||
|
const aiScene* scene = importer.ReadFile(_path.data(),
|
||||||
|
/*aiProcess_CalcTangentSpace |*/
|
||||||
|
aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs |
|
||||||
|
aiProcess_CalcTangentSpace);
|
||||||
|
|
||||||
|
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
|
||||||
|
{
|
||||||
|
spdlog::error("failed to load model {}: {}", _path, importer.GetErrorString());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto model = std::make_shared<Model>();
|
||||||
|
|
||||||
|
process_ai_node(scene->mRootNode, scene, model.get());
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_ai_node(aiNode* node, const aiScene* scene, Model* model)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < node->mNumMeshes; i++)
|
||||||
|
{
|
||||||
|
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
|
||||||
|
model->meshes.push_back(process_ai_mesh(mesh, scene, model));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh process_ai_mesh(aiMesh* aiMesh, const aiScene* scene, Model* model)
|
||||||
|
{
|
||||||
|
auto positions = std::vector<float>(aiMesh->mNumVertices * 3);
|
||||||
|
auto uvs = std::vector<float>(aiMesh->mNumVertices * 2);
|
||||||
|
auto normals = std::vector<float>(aiMesh->mNumVertices * 3);
|
||||||
|
auto indices = std::vector<unsigned int>(aiMesh->mNumFaces * 3);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < aiMesh->mNumVertices; i++)
|
||||||
|
{
|
||||||
|
auto position = aiMesh->mVertices[i];
|
||||||
|
positions.push_back(position.x);
|
||||||
|
positions.push_back(position.y);
|
||||||
|
positions.push_back(position.z);
|
||||||
|
|
||||||
|
if (aiMesh->HasNormals())
|
||||||
|
{
|
||||||
|
normals.push_back(aiMesh->mNormals[i].x);
|
||||||
|
normals.push_back(aiMesh->mNormals[i].y);
|
||||||
|
normals.push_back(aiMesh->mNormals[i].z);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
normals.push_back(0);
|
||||||
|
normals.push_back(0);
|
||||||
|
normals.push_back(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aiMesh->HasTextureCoords(0))
|
||||||
|
{
|
||||||
|
uvs.push_back(aiMesh->mTextureCoords[0][i].x);
|
||||||
|
uvs.push_back(aiMesh->mTextureCoords[0][i].y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uvs.push_back(0);
|
||||||
|
uvs.push_back(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned int i = 0; i < aiMesh->mNumFaces; i++)
|
||||||
|
{
|
||||||
|
const aiFace face = aiMesh->mFaces[i];
|
||||||
|
for(unsigned int j = 0; j < face.mNumIndices; j++)
|
||||||
|
indices.push_back(face.mIndices[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {positions, uvs, normals, indices};
|
||||||
|
}
|
||||||
20
src/ModelLoader.h
Normal file
20
src/ModelLoader.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// Created by lbmas on 4/29/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef B_ENGINE_MODELLOADER_H
|
||||||
|
#define B_ENGINE_MODELLOADER_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "Model.h"
|
||||||
|
|
||||||
|
class ModelLoader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<Model> load_from_file(std::string_view _path);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //B_ENGINE_MODELLOADER_H
|
||||||
128
src/ShaderProgram.cpp
Normal file
128
src/ShaderProgram.cpp
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
//
|
||||||
|
// Created by lbmas on 4/29/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ShaderProgram.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "glad/gl.h"
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
void log_shader_compile_errors(GLuint shader, std::string_view shaderType);
|
||||||
|
void log_program_compile_errors(GLuint program);
|
||||||
|
|
||||||
|
std::shared_ptr<ShaderProgram> ShaderProgram::load(std::string_view vertexPath, std::string_view fragmentPath)
|
||||||
|
{
|
||||||
|
std::string vertexSource;
|
||||||
|
std::string fragmentSource;
|
||||||
|
|
||||||
|
std::ifstream vertexFile {};
|
||||||
|
std::ifstream fragmentFile {};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
vertexFile.open(vertexPath.data());
|
||||||
|
fragmentFile.open(fragmentPath.data());
|
||||||
|
|
||||||
|
ss << vertexFile.rdbuf();
|
||||||
|
vertexSource = ss.str();
|
||||||
|
ss.clear();
|
||||||
|
ss << fragmentFile.rdbuf();
|
||||||
|
fragmentSource = ss.str();
|
||||||
|
|
||||||
|
vertexFile.close();
|
||||||
|
fragmentFile.close();
|
||||||
|
} catch (std::ifstream::failure& e) {
|
||||||
|
spdlog::error("Failed to load shader program {}, {}: {}", vertexPath, fragmentPath, std::string(e.what()).c_str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int vertex;
|
||||||
|
unsigned int fragment;
|
||||||
|
|
||||||
|
vertex = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
glShaderSource(vertex, 1, reinterpret_cast<const GLchar* const*>(vertexSource.data()), NULL);
|
||||||
|
glCompileShader(vertex);
|
||||||
|
log_shader_compile_errors(vertex, "vertex");
|
||||||
|
|
||||||
|
fragment = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
glShaderSource(fragment, 1, reinterpret_cast<const GLchar* const*>(fragmentSource.data()), NULL);
|
||||||
|
glCompileShader(fragment);
|
||||||
|
log_shader_compile_errors(fragment, "fragment");
|
||||||
|
|
||||||
|
ShaderProgram program;
|
||||||
|
program.id = glCreateProgram();
|
||||||
|
|
||||||
|
glAttachShader(program.id, vertex);
|
||||||
|
glAttachShader(program.id, fragment);
|
||||||
|
glLinkProgram(program.id);
|
||||||
|
log_program_compile_errors(program.id);
|
||||||
|
|
||||||
|
glDeleteShader(vertex);
|
||||||
|
glDeleteShader(fragment);
|
||||||
|
|
||||||
|
return std::make_shared<ShaderProgram>(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderProgram::bind()
|
||||||
|
{
|
||||||
|
glUseProgram(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderProgram::unbind()
|
||||||
|
{
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderProgram::setFloat(std::string_view name, float value) const
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderProgram::setVec2(std::string_view name, const glm::vec2& value) const
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderProgram::setVec3(std::string_view name, const glm::vec3& value) const
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderProgram::setVec4(std::string_view name, const glm::vec4& value) const
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderProgram::setMat4(std::string_view name, const glm::mat4& value) const
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_shader_compile_errors(GLuint shader, std::string_view shaderType)
|
||||||
|
{
|
||||||
|
GLint success;
|
||||||
|
GLchar infoLog[1024];
|
||||||
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
|
||||||
|
spdlog::error("Failed to compile {} shader: {}", shaderType, infoLog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_program_compile_errors(GLuint program)
|
||||||
|
{
|
||||||
|
GLint success;
|
||||||
|
GLchar infoLog[1024];
|
||||||
|
glGetProgramiv(program, GL_COMPILE_STATUS, &success);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
glGetShaderInfoLog(program, 1024, NULL, infoLog);
|
||||||
|
spdlog::error("Failed to compile program: {}", infoLog);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/ShaderProgram.h
Normal file
33
src/ShaderProgram.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// Created by lbmas on 4/29/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef B_ENGINE_SHADERPROGRAM_H
|
||||||
|
#define B_ENGINE_SHADERPROGRAM_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "glad/gl.h"
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
class ShaderProgram
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<ShaderProgram> load(std::string_view vertexPath, std::string_view fragmentPath);
|
||||||
|
|
||||||
|
void bind();
|
||||||
|
void unbind();
|
||||||
|
|
||||||
|
void setFloat(std::string_view name, float value) const;
|
||||||
|
void setVec2(std::string_view name, const glm::vec2 &value) const;
|
||||||
|
void setVec3(std::string_view name, const glm::vec3 &value) const;
|
||||||
|
void setMat4(std::string_view name, const glm::mat4 &mat) const;
|
||||||
|
void setVec4(std::string_view name, const glm::vec4 &value) const;
|
||||||
|
private:
|
||||||
|
GLuint id = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //B_ENGINE_SHADERPROGRAM_H
|
||||||
82
src/Texture.cpp
Normal file
82
src/Texture.cpp
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
//
|
||||||
|
// Created by lbmas on 4/29/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Texture.h"
|
||||||
|
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
#include "glad/gl.h"
|
||||||
|
|
||||||
|
std::shared_ptr<Texture> Texture::load_from_file(std::string_view filePath)
|
||||||
|
{
|
||||||
|
int width, height, channels;
|
||||||
|
unsigned char *data = stbi_load(filePath.data(), &width, &height, &channels, 0);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
spdlog::error("Failed to load texture from file {}", filePath);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture texture = {};
|
||||||
|
glGenTextures(1, &texture.id);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture.id);
|
||||||
|
|
||||||
|
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_WRAP_S, GL_REPEAT);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||||
|
|
||||||
|
const GLenum format = (channels == 4) ? GL_RGBA : GL_RGB;
|
||||||
|
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
stbi_image_free(data);
|
||||||
|
|
||||||
|
texture.filePath = filePath;
|
||||||
|
texture.width = width;
|
||||||
|
texture.height = height;
|
||||||
|
texture.channels = channels;
|
||||||
|
return std::make_shared<Texture>(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Texture> Texture::load_from_data(unsigned char* data, int width, int height, int channels)
|
||||||
|
{
|
||||||
|
Texture texture = {};
|
||||||
|
glGenTextures(1, &texture.id);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture.id);
|
||||||
|
|
||||||
|
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_WRAP_S, GL_REPEAT);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||||
|
|
||||||
|
const GLenum format = (channels == 4) ? GL_RGBA : GL_RGB;
|
||||||
|
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
texture.filePath = "";
|
||||||
|
texture.width = width;
|
||||||
|
texture.height = height;
|
||||||
|
texture.channels = channels;
|
||||||
|
return std::make_shared<Texture>(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture::~Texture()
|
||||||
|
{
|
||||||
|
glDeleteTextures(1, &id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::bind(unsigned int slot)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::unbind()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
36
src/Texture.h
Normal file
36
src/Texture.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// Created by lbmas on 4/29/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef B_ENGINE_TEXTURE_H
|
||||||
|
#define B_ENGINE_TEXTURE_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string_view>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Texture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<Texture> load_from_file(std::string_view filePath);
|
||||||
|
static std::shared_ptr<Texture> load_from_data(unsigned char* data, int width, int height, int channels);
|
||||||
|
|
||||||
|
Texture() = default;
|
||||||
|
~Texture();
|
||||||
|
|
||||||
|
void bind(unsigned int slot);
|
||||||
|
void unbind();
|
||||||
|
|
||||||
|
[[nodiscard]] int get_width() const { return width; }
|
||||||
|
[[nodiscard]] int get_height() const { return height; }
|
||||||
|
[[nodiscard]] int get_channels() const { return channels; }
|
||||||
|
private:
|
||||||
|
unsigned int id = 0;
|
||||||
|
std::string filePath;
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
int channels = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //B_ENGINE_TEXTURE_H
|
||||||
145
src/main.cpp
145
src/main.cpp
@@ -1,55 +1,72 @@
|
|||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "glad/gl.h"
|
#include "glad/gl.h"
|
||||||
#include "GLFW/glfw3.h"
|
#include "GLFW/glfw3.h"
|
||||||
|
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
GLFWwindow* gWindow;
|
#include "glm/glm.hpp"
|
||||||
|
#include "glm/ext/matrix_clip_space.hpp"
|
||||||
|
#include "glm/ext/matrix_transform.hpp"
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
#include "Model.h"
|
||||||
|
#include "ShaderProgram.h"
|
||||||
|
#include "Texture.h"
|
||||||
|
|
||||||
|
GLFWwindow* gWindow = nullptr;
|
||||||
int gWindowWidth = 800;
|
int gWindowWidth = 800;
|
||||||
int gWindowHeight = 600;
|
int gWindowHeight = 600;
|
||||||
|
|
||||||
int glVersionMajor;
|
int glVersionMajor = 0;
|
||||||
int glVersionMinor;
|
int glVersionMinor = 0;
|
||||||
|
|
||||||
|
auto cameraPosition = glm::vec3{0, 0, 0};
|
||||||
|
auto view = glm::mat4{0};
|
||||||
|
auto projection = glm::mat4{0};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<Model>> gModels = {};
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<Texture>> gTextures = {};
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<ShaderProgram>> gShaders = {};
|
||||||
|
|
||||||
void glfw_error_callback(int error, const char* description);
|
void glfw_error_callback(int error, const char* description);
|
||||||
void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
|
void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
|
||||||
void glfw_framebuffer_size_callback(GLFWwindow* window, int width, int height);
|
void glfw_framebuffer_size_callback(GLFWwindow* window, int width, int height);
|
||||||
|
|
||||||
|
void init_glfw();
|
||||||
|
void create_main_window();
|
||||||
|
void load_gl();
|
||||||
|
void init_camera();
|
||||||
|
|
||||||
|
std::shared_ptr<Model> load_model(std::string_view _path);
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
spdlog::info("b_engine start");
|
spdlog::info("b_engine start");
|
||||||
|
|
||||||
if (!glfwInit()) {
|
init_glfw();
|
||||||
spdlog::error("could not initialize glfw");
|
|
||||||
|
create_main_window();
|
||||||
|
|
||||||
|
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};
|
||||||
|
gTextures["default_diffuse"] = Texture::load_from_data(reinterpret_cast<unsigned char*>(&defaultDiffuseData), 1, 1, 4);
|
||||||
|
|
||||||
|
// create a default specular map
|
||||||
|
unsigned char defaultSpecularData[4] = {static_cast<unsigned char>(128), 128, 128, 255};
|
||||||
|
gTextures["default_specular"] = Texture::load_from_data(reinterpret_cast<unsigned char*>(&defaultSpecularData), 1, 1, 4);
|
||||||
|
|
||||||
|
gShaders["phong_shader"] = ShaderProgram::load("./resources/standard.frag", "./resources/standard.vert");
|
||||||
|
if (gShaders["phong_shader"] == nullptr)
|
||||||
|
{
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
|
||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
||||||
|
|
||||||
glfwSetErrorCallback(glfw_error_callback);
|
|
||||||
|
|
||||||
gWindow = glfwCreateWindow(gWindowWidth, gWindowHeight, "b_engine v0.0.1", nullptr, nullptr);
|
|
||||||
if (!gWindow) {
|
|
||||||
spdlog::error("failed to create glfw window");
|
|
||||||
std::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
glfwMakeContextCurrent(gWindow);
|
|
||||||
|
|
||||||
glfwSetKeyCallback(gWindow, glfw_key_callback);
|
|
||||||
glfwSetFramebufferSizeCallback(gWindow, glfw_framebuffer_size_callback);
|
|
||||||
|
|
||||||
int version = gladLoadGL(glfwGetProcAddress);
|
|
||||||
glVersionMajor = GLAD_VERSION_MAJOR(version);
|
|
||||||
glVersionMinor = GLAD_VERSION_MINOR(version);
|
|
||||||
spdlog::info("gl version {}.{}", glVersionMajor, glVersionMinor);
|
|
||||||
|
|
||||||
int fbWidth, fbHeight;
|
|
||||||
glfwGetFramebufferSize(gWindow, &fbWidth, &fbHeight);
|
|
||||||
glViewport(0, 0, fbWidth, fbHeight);
|
|
||||||
|
|
||||||
while (!glfwWindowShouldClose(gWindow)) {
|
while (!glfwWindowShouldClose(gWindow)) {
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
|
|
||||||
@@ -78,3 +95,65 @@ void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, in
|
|||||||
void glfw_framebuffer_size_callback(GLFWwindow *window, int width, int height) {
|
void glfw_framebuffer_size_callback(GLFWwindow *window, int width, int height) {
|
||||||
glViewport(0, 0, width, height);
|
glViewport(0, 0, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void init_glfw()
|
||||||
|
{
|
||||||
|
if (!glfwInit()) {
|
||||||
|
spdlog::error("could not initialize glfw");
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
|
|
||||||
|
glfwSetErrorCallback(glfw_error_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_main_window()
|
||||||
|
{
|
||||||
|
gWindow = glfwCreateWindow(gWindowWidth, gWindowHeight, "b_engine v0.0.1", nullptr, nullptr);
|
||||||
|
if (!gWindow) {
|
||||||
|
spdlog::error("failed to create glfw window");
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwMakeContextCurrent(gWindow);
|
||||||
|
|
||||||
|
glfwSetKeyCallback(gWindow, glfw_key_callback);
|
||||||
|
glfwSetFramebufferSizeCallback(gWindow, glfw_framebuffer_size_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_gl()
|
||||||
|
{
|
||||||
|
int version = gladLoadGL(glfwGetProcAddress);
|
||||||
|
glVersionMajor = GLAD_VERSION_MAJOR(version);
|
||||||
|
glVersionMinor = GLAD_VERSION_MINOR(version);
|
||||||
|
spdlog::info("gl version {}.{}", glVersionMajor, glVersionMinor);
|
||||||
|
|
||||||
|
if (gWindow)
|
||||||
|
{
|
||||||
|
int fbWidth, fbHeight;
|
||||||
|
glfwGetFramebufferSize(gWindow, &fbWidth, &fbHeight);
|
||||||
|
glViewport(0, 0, fbWidth, fbHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_camera()
|
||||||
|
{
|
||||||
|
cameraPosition = glm::vec3{0, 5, 5};
|
||||||
|
|
||||||
|
auto cameraTarget = glm::vec3{0, 0, 0};
|
||||||
|
auto behind = glm::normalize(cameraPosition - cameraTarget);
|
||||||
|
auto right = glm::normalize(glm::cross(glm::vec3{0, 1, 0}, behind));
|
||||||
|
auto up = glm::normalize(glm::cross(right, behind));
|
||||||
|
|
||||||
|
view = glm::lookAt(cameraPosition, cameraTarget, up);
|
||||||
|
float aspectRatio = static_cast<float>(gWindowWidth) / static_cast<float>(gWindowHeight);
|
||||||
|
projection = glm::perspective(glm::radians(75.f), aspectRatio, 0.1f, 100.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Model> load_model(std::string_view _path)
|
||||||
|
{
|
||||||
|
return std::make_shared<Model>();
|
||||||
|
}
|
||||||
7988
src/stb_image.h
Normal file
7988
src/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user