started on assimp importing and texture stuff

This commit is contained in:
2026-05-01 20:06:54 -05:00
parent cb1d8f753d
commit f9010eea81
20 changed files with 8673 additions and 42 deletions

5
src/Material.cpp Normal file
View File

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

30
src/Material.h Normal file
View 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

View File

@@ -3,8 +3,9 @@
//
#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;
ebo = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
@@ -25,4 +26,24 @@ Mesh::Mesh(const std::vector<float> &positions, const std::vector<float> &uvs, c
glGenBuffers(1, &normalVbo);
glBindBuffer(GL_ARRAY_BUFFER, normalVbo);
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);
}

View File

@@ -16,11 +16,10 @@ public:
Mesh(const std::vector<float>& positions,
const std::vector<float>& uvs,
const std::vector<float>& normals,
const std::vector<float>& indices);
const std::vector<unsigned int>& indices);
void bind();
private:
GLuint vao;
GLuint ebo;
};
#endif //B_ENGINE_MESH_H

5
src/Model.cpp Normal file
View File

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

22
src/Model.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View File

@@ -1,55 +1,72 @@
#include <iostream>
#include "glad/gl.h"
#include "GLFW/glfw3.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 gWindowHeight = 600;
int glVersionMajor;
int glVersionMinor;
int glVersionMajor = 0;
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_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
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() {
spdlog::info("b_engine start");
if (!glfwInit()) {
spdlog::error("could not initialize glfw");
init_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);
}
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)) {
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) {
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

File diff suppressed because it is too large Load Diff