Implement model loading with assimp

This commit is contained in:
Abdelrahman Said 2024-12-26 00:59:00 +00:00
parent 806c260893
commit d8d2c73438
11 changed files with 199672 additions and 44 deletions

View File

@ -1,9 +1,9 @@
#!/bin/bash #!/bin/bash
CC=clang CC=clang
CFLAGS="-g -c -Wall -Isrc/glad/include" CFLAGS="-O3 -c -Wall -Isrc/glad/include"
CXX=clang++ CXX=clang++
CXXFLAGS="-g -Wall -std=c++20 $(pkg-config --cflags sdl2) -Isrc/glad/include -Isrc/glm -Isrc/assimp/include -Isrc/assimp/build/include" CXXFLAGS="-O3 -Wall -std=c++20 $(pkg-config --cflags sdl2) -Isrc/glad/include -Isrc/glm -Isrc/assimp/include -Isrc/assimp/build/include"
LIBS="$(pkg-config --libs sdl2) -ldl -lz -lminizip -Lsrc/assimp/build/lib/ -lassimp" LIBS="$(pkg-config --libs sdl2) -ldl -lz -lminizip -Lsrc/assimp/build/lib/ -lassimp"
GLAD_SRC="src/glad/src/glad.c" GLAD_SRC="src/glad/src/glad.c"
GLAD_OBJ="glad.o" GLAD_OBJ="glad.o"
@ -14,5 +14,5 @@ OUT=main
(set -x ; $CXX $CXXFLAGS $SRC $LIBS -o $OUT) (set -x ; $CXX $CXXFLAGS $SRC $LIBS -o $OUT)
if [[ -f $GLAD_OBJ ]]; then if [[ -f $GLAD_OBJ ]]; then
rm $GLAD_OBJ rm $GLAD_OBJ
fi fi

BIN
models/backpack/ao.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@ -0,0 +1,16 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl Scene_-_Root
Ns 225.000000
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.0 0.0 0.0
Ni 1.450000
d 1.000000
illum 2
map_Kd diffuse.jpg
map_Bump normal.png
map_Ks specular.jpg

199481
models/backpack/backpack.obj Normal file

File diff suppressed because it is too large Load Diff

BIN
models/backpack/diffuse.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 MiB

BIN
models/backpack/normal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

View File

@ -0,0 +1,3 @@
Model by Berk Gedik, from: https://sketchfab.com/3d-models/survival-guitar-backpack-low-poly-799f8c4511f84fab8c3f12887f7e6b36
Modified material assignment (Joey de Vries) for easier load in OpenGL model loading chapter, and renamed albedo to diffuse and metallic to specular to match non-PBR lighting setup.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 MiB

View File

@ -3,8 +3,8 @@
#define POINT_LIGHT_COUNT 4 #define POINT_LIGHT_COUNT 4
struct Material { struct Material {
sampler2D diffuse; sampler2D diffuse1;
sampler2D specular; sampler2D specular1;
float shininess; float shininess;
}; };
@ -70,11 +70,11 @@ vec3 calc_dir_light(DirLight light, vec3 normal, vec3 view_direction) {
vec3 light_direction = normalize(-light.direction); vec3 light_direction = normalize(-light.direction);
vec3 reflect_direction = reflect(-light_direction, normal); vec3 reflect_direction = reflect(-light_direction, normal);
float diff = max(dot(normal, light_direction), 0.0); float diff = max(dot(normal, light_direction), 0.0);
vec3 diff_tex = vec3(texture(material.diffuse, uv_coords)); vec3 diff_tex = vec3(texture(material.diffuse1, uv_coords));
float spec = pow(max(dot(reflect_direction, view_direction), 0.0), material.shininess); float spec = pow(max(dot(reflect_direction, view_direction), 0.0), material.shininess);
vec3 ambient = light.ambient * diff_tex; vec3 ambient = light.ambient * diff_tex;
vec3 diffuse = light.diffuse * (diff * diff_tex); vec3 diffuse = light.diffuse * (diff * diff_tex);
vec3 specular = light.specular * (spec * vec3(texture(material.specular, uv_coords))); vec3 specular = light.specular * (spec * vec3(texture(material.specular1, uv_coords)));
return ambient + diffuse + specular; return ambient + diffuse + specular;
} }
@ -85,11 +85,11 @@ vec3 calc_point_light(PointLight light, vec3 normal, vec3 frag_position, vec3 vi
float distance = length(light.position - frag_position); float distance = length(light.position - frag_position);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
float diff = max(dot(normal, light_direction), 0.0); float diff = max(dot(normal, light_direction), 0.0);
vec3 diff_tex = vec3(texture(material.diffuse, uv_coords)); vec3 diff_tex = vec3(texture(material.diffuse1, uv_coords));
float spec = pow(max(dot(reflect_direction, view_direction), 0.0), material.shininess); float spec = pow(max(dot(reflect_direction, view_direction), 0.0), material.shininess);
vec3 ambient = light.ambient * diff_tex * attenuation; vec3 ambient = light.ambient * diff_tex * attenuation;
vec3 diffuse = light.diffuse * (diff * diff_tex) * attenuation; vec3 diffuse = light.diffuse * (diff * diff_tex) * attenuation;
vec3 specular = light.specular * (spec * vec3(texture(material.specular, uv_coords))) * attenuation; vec3 specular = light.specular * (spec * vec3(texture(material.specular1, uv_coords))) * attenuation;
return ambient + diffuse + specular; return ambient + diffuse + specular;
} }
@ -102,10 +102,10 @@ vec3 calc_spot_light(SpotLight light, vec3 normal, vec3 frag_position, vec3 view
float intensity = clamp((theta - light.outer_cutoff) / epsilon, 0.0, 1.0); float intensity = clamp((theta - light.outer_cutoff) / epsilon, 0.0, 1.0);
float diff = max(dot(normal, light_direction), 0.0); float diff = max(dot(normal, light_direction), 0.0);
float spec = pow(max(dot(reflect_direction, view_direction), 0.0), material.shininess); float spec = pow(max(dot(reflect_direction, view_direction), 0.0), material.shininess);
vec3 diff_tex = vec3(texture(material.diffuse, uv_coords)); vec3 diff_tex = vec3(texture(material.diffuse1, uv_coords));
vec3 ambient = light.ambient * diff_tex; vec3 ambient = light.ambient * diff_tex;
vec3 diffuse = light.diffuse * (diff * diff_tex) * intensity; vec3 diffuse = light.diffuse * (diff * diff_tex) * intensity;
vec3 specular = light.specular * (spec * vec3(texture(material.specular, uv_coords))) * intensity; vec3 specular = light.specular * (spec * vec3(texture(material.specular1, uv_coords))) * intensity;
return ambient + diffuse + specular; return ambient + diffuse + specular;
} }

View File

@ -14,6 +14,14 @@
#include "glm/gtx/rotate_vector.hpp" #include "glm/gtx/rotate_vector.hpp"
#include "glm/gtx/string_cast.hpp" #include "glm/gtx/string_cast.hpp"
// Assimp
#include "assimp/Importer.hpp"
#include "assimp/postprocess.h"
#include "assimp/scene.h"
#include "assimp/mesh.h"
#include "assimp/material.h"
#include "assimp/types.h"
// SDL // SDL
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <SDL2/SDL_timer.h> #include <SDL2/SDL_timer.h>
@ -67,16 +75,23 @@ class Shader {
static const char *get_shader_type_string(GLenum shader_type); static const char *get_shader_type_string(GLenum shader_type);
}; };
enum TextureType : unsigned char {
TEXTURE_TYPE_DIFFUSE,
TEXTURE_TYPE_SPECULAR,
};
class Texture2D { class Texture2D {
public: public:
Texture2D(const char *filename, GLint texture_unit, const char *name); Texture2D(const char *filename, GLint texture_unit, TextureType type);
~Texture2D(); ~Texture2D();
void activate() const; void activate() const;
std::string name(unsigned int index) const;
int width; int width;
int height; int height;
int channels; int channels;
const char *filename;
GLint texture_unit; GLint texture_unit;
std::string name; TextureType type;
private: private:
GLuint texture; GLuint texture;
GLint format; GLint format;
@ -101,6 +116,25 @@ class Mesh {
void setup_mesh(); void setup_mesh();
}; };
class Model {
public:
Model(const char *path) {
load_model(path);
}
void draw(Shader &shader);
private:
std::vector<Texture2D> loaded_textures;
std::vector<Mesh> meshes;
std::string directory;
GLint texture_unit = GL_TEXTURE0;
void load_model(std::string path);
void process_node(aiNode *node, const aiScene *scene);
Mesh process_mesh(aiMesh *mesh, const aiScene *scene);
std::vector<Texture2D> load_material_textures(aiMaterial *mat, aiTextureType type);
};
int main() { int main() {
if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
return EXIT_CODE_SDL_INIT_FAILED; return EXIT_CODE_SDL_INIT_FAILED;
@ -179,12 +213,7 @@ int main() {
13, 15, 16 13, 15, 16
}; };
std::vector<Texture2D> textures = { Model backpack = {"models/backpack/backpack.obj"};
Texture2D("images/container2.png", GL_TEXTURE0, "material.diffuse"),
Texture2D("images/container2_specular.png", GL_TEXTURE1, "material.specular"),
};
Mesh container = {vertices, indices, textures};
Mesh light = {vertices, indices, {}}; Mesh light = {vertices, indices, {}};
Shader main_shader {"shaders/vert.glsl", "shaders/frag.glsl"}; Shader main_shader {"shaders/vert.glsl", "shaders/frag.glsl"};
@ -207,24 +236,10 @@ int main() {
glm::mat3 normal_mat = glm::mat3(1.0f); glm::mat3 normal_mat = glm::mat3(1.0f);
main_shader.set_mat4 ("projection", projection); main_shader.set_mat4 ("projection", projection);
main_shader.set_vec3 ("material.specular", glm::vec3(0.5f, 0.5f, 0.5f));
main_shader.set_float("material.shininess", 32.0f); main_shader.set_float("material.shininess", 32.0f);
light_shader.set_mat4("projection", projection); light_shader.set_mat4("projection", projection);
std::vector<glm::vec3> cube_positions = {
glm::vec3( 0.0f, 0.0f, 0.0f),
glm::vec3( 4.0f, 6.0f, -15.0f),
glm::vec3(-3.5f, -3.2f, -2.5f),
glm::vec3(-5.8f, -3.0f, -12.3f),
glm::vec3( 4.4f, -1.4f, -3.5f),
glm::vec3(-3.7f, 4.0f, -7.5f),
glm::vec3( 3.3f, -3.0f, -2.5f),
glm::vec3( 3.5f, 3.0f, -2.5f),
glm::vec3( 3.5f, 1.2f, -1.5f),
glm::vec3(-3.3f, 2.0f, -1.5f)
};
std::vector<glm::vec3> point_light_positions = { std::vector<glm::vec3> point_light_positions = {
glm::vec3( 0.7f, 0.2f, 2.0f), glm::vec3( 0.7f, 0.2f, 2.0f),
glm::vec3( 2.3f, -3.3f, -4.0f), glm::vec3( 2.3f, -3.3f, -4.0f),
@ -364,15 +379,12 @@ int main() {
glClearColor(0.04f, 0.08f, 0.08f, 1.0f); glClearColor(0.04f, 0.08f, 0.08f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for (int i = 0; i < cube_positions.size(); ++i) { model = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f));
model = glm::translate(glm::mat4(1.0f), cube_positions[i]); normal_mat = glm::transpose(glm::inverse(model));
model = glm::rotate(model, glm::radians(10.0f * i), glm::vec3(1.0f, 0.3f, 0.5f)); main_shader.activate();
normal_mat = glm::transpose(glm::inverse(model)); main_shader.set_mat4("model", model);
main_shader.activate(); main_shader.set_mat3("normal_mat", normal_mat);
main_shader.set_mat4("model", model); backpack.draw(main_shader);
main_shader.set_mat3("normal_mat", normal_mat);
container.draw(main_shader);
}
// Draw light source // Draw light source
for (int i = 0; i < point_light_positions.size(); ++i) { for (int i = 0; i < point_light_positions.size(); ++i) {
@ -531,7 +543,7 @@ const char *Shader::get_shader_type_string(GLenum shader_type) {
return output; return output;
} }
Texture2D::Texture2D(const char *filename, GLint texture_unit, const char *name) : texture_unit(texture_unit), name(name) { Texture2D::Texture2D(const char *filename, GLint texture_unit, TextureType type) : filename(filename), texture_unit(texture_unit), type(type) {
uint8_t *image = stbi_load(filename, &width, &height, &channels, 0); uint8_t *image = stbi_load(filename, &width, &height, &channels, 0);
if (!image) { if (!image) {
return; return;
@ -562,6 +574,23 @@ void Texture2D::activate() const {
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
} }
std::string Texture2D::name(unsigned int index) const {
std::string output = "material.";
switch (type) {
case TEXTURE_TYPE_DIFFUSE:
output += "diffuse";
break;
case TEXTURE_TYPE_SPECULAR:
output += "specular";
break;
}
output += std::to_string(index);
return output.c_str();
}
Mesh::Mesh(std::vector<Vertex> vertices, std::vector<GLuint> indices, std::vector<Texture2D> textures) Mesh::Mesh(std::vector<Vertex> vertices, std::vector<GLuint> indices, std::vector<Texture2D> textures)
: vertices(vertices), indices(indices), textures(textures) { : vertices(vertices), indices(indices), textures(textures) {
setup_mesh(); setup_mesh();
@ -589,9 +618,14 @@ void Mesh::setup_mesh() {
} }
void Mesh::draw(Shader &shader) { void Mesh::draw(Shader &shader) {
unsigned int diffuse = 1;
unsigned int specular = 1;
unsigned int index;
for (int i = 0; i < textures.size(); ++i) { for (int i = 0; i < textures.size(); ++i) {
const Texture2D &texture = textures[i]; const Texture2D &texture = textures[i];
shader.set_int(texture.name.c_str(), texture.texture_unit - GL_TEXTURE0); index = texture.type == TEXTURE_TYPE_DIFFUSE ? diffuse++ : specular++;
shader.set_int(texture.name(index).c_str(), texture.texture_unit - GL_TEXTURE0);
texture.activate(); texture.activate();
} }
@ -599,3 +633,97 @@ void Mesh::draw(Shader &shader) {
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, (void *)0); glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, (void *)0);
glBindVertexArray(0); glBindVertexArray(0);
} }
void Model::draw(Shader &shader) {
for (int i = 0; i < meshes.size(); ++i) {
meshes[i].draw(shader);
}
}
void Model::load_model(std::string path) {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
printf("Failed to load model: %s. Error: %s\n", path.c_str(), importer.GetErrorString());
return;
}
directory = path.substr(0, path.find_last_of('/'));
process_node(scene->mRootNode, scene);
}
void Model::process_node(aiNode *node, const aiScene *scene) {
for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(process_mesh(mesh, scene));
}
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
process_node(node->mChildren[i], scene);
}
}
Mesh Model::process_mesh(aiMesh *mesh, const aiScene *scene) {
std::vector<Vertex> vertices;
std::vector<GLuint> indices;
std::vector<Texture2D> textures;
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
Vertex vertex;
vertex.position = glm::vec3(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);
vertex.normal = glm::vec3(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);
if (mesh->mTextureCoords[0]) {
vertex.tex_coord = glm::vec2(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y);
} else {
vertex.tex_coord = glm::vec2(0.0f, 0.0f);
}
vertices.push_back(vertex);
}
for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
aiFace face = mesh->mFaces[i];
for (unsigned int j = 0; j < face.mNumIndices; ++j) {
indices.push_back(face.mIndices[j]);
}
}
if (mesh->mMaterialIndex >= 0) {
aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
std::vector<Texture2D> diffuse_maps = load_material_textures(material, aiTextureType_DIFFUSE);
textures.insert(textures.end(), diffuse_maps.begin(), diffuse_maps.end());
std::vector<Texture2D> specular_maps = load_material_textures(material, aiTextureType_SPECULAR);
textures.insert(textures.end(), specular_maps.begin(), specular_maps.end());
}
return Mesh(vertices, indices, textures);
}
std::vector<Texture2D> Model::load_material_textures(aiMaterial *material, aiTextureType type) {
std::vector<Texture2D> textures;
for (unsigned int i = 0; i < material->GetTextureCount(type); ++i) {
aiString path;
material->GetTexture(type, i, &path);
std::string absolute_path = directory + '/' + path.C_Str();
bool skip = false;
for (unsigned int j = 0; i < loaded_textures.size(); ++j) {
if (std::strcmp(loaded_textures[j].filename, absolute_path.c_str()) == 0) {
textures.push_back(loaded_textures[j]);
skip = true;
break;
}
}
if (!skip) {
TextureType tex_type = type == aiTextureType_DIFFUSE ? TEXTURE_TYPE_DIFFUSE : TEXTURE_TYPE_SPECULAR;
Texture2D texture = {absolute_path.c_str(), texture_unit++, tex_type};
textures.push_back(texture);
}
}
return textures;
}