|
|
|
@ -12,6 +12,7 @@
@@ -12,6 +12,7 @@
|
|
|
|
|
#include <shaders.h> |
|
|
|
|
#include <spdlog/spdlog.h> |
|
|
|
|
#include <stb_image.h> |
|
|
|
|
#include <vfs.hpp> |
|
|
|
|
|
|
|
|
|
Mesh::Mesh(std::vector<struct vertex> vertices, |
|
|
|
|
std::vector<unsigned int> indices, |
|
|
|
@ -225,36 +226,58 @@ void Model::draw(std::shared_ptr<ubo_proxy<material_properties>> ubo_mat,
@@ -225,36 +226,58 @@ void Model::draw(std::shared_ptr<ubo_proxy<material_properties>> ubo_mat,
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Model::load_model(std::string path) { |
|
|
|
|
spdlog::info("Loading model {}", path); |
|
|
|
|
const aiScene *scene = importer.ReadFile( |
|
|
|
|
path, aiProcess_Triangulate | aiProcess_FlipUVs | |
|
|
|
|
aiProcess_CalcTangentSpace | aiProcess_GenNormals | |
|
|
|
|
aiProcess_OptimizeMeshes); |
|
|
|
|
spdlog::error("UNSUPPORTED MODEL LOAD NOOP"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
expected<model, error> model_loader::load_model(vfs::fsmount &fs, |
|
|
|
|
vfs::zpath_view path) { |
|
|
|
|
spdlog::info("Loading model {}", path.str()); |
|
|
|
|
|
|
|
|
|
auto data = |
|
|
|
|
fs.open_read(path).and_then([](auto a) -> auto { return a->read(); }); |
|
|
|
|
|
|
|
|
|
if (!data.has_value()) { |
|
|
|
|
unexpected(data.error()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Assimp::Importer importer; |
|
|
|
|
const aiScene *scene = importer.ReadFileFromMemory( |
|
|
|
|
data->data(), data->size(), |
|
|
|
|
aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace | |
|
|
|
|
aiProcess_GenNormals | aiProcess_OptimizeMeshes, |
|
|
|
|
path.ext().c_str()); |
|
|
|
|
|
|
|
|
|
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || |
|
|
|
|
!scene->mRootNode) { |
|
|
|
|
std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl; |
|
|
|
|
throw std::exception(); |
|
|
|
|
return; |
|
|
|
|
unexpected(error(fmt::format("ASSIMP::{}", importer.GetErrorString()))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
directory = path.substr(0, path.find_last_of('/')); |
|
|
|
|
// set the root transform
|
|
|
|
|
// https://assimp-docs.readthedocs.io/en/v5.1.0/usage/use_the_lib.html#the-node-hierarchy
|
|
|
|
|
// AssImp has a transformation for each mesh; which we should use
|
|
|
|
|
// But hopefully just getting the root is helpful enough
|
|
|
|
|
// We are basically not supporting collada etc scenese with a complex heirachy
|
|
|
|
|
// of meshes
|
|
|
|
|
// of meshes which i think store the transforms in animation bones
|
|
|
|
|
float scale; |
|
|
|
|
root_transform = mat4from_aimat(scene->mRootNode->mTransformation); |
|
|
|
|
auto root_transform = mat4from_aimat(scene->mRootNode->mTransformation); |
|
|
|
|
if (scene->mMetaData->Get("UnitScaleFactor", scale)) { |
|
|
|
|
spdlog::info("Scale factor {}", scale); |
|
|
|
|
root_transform = glm::scale(root_transform, glm::vec3(1 / scale)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
process_node(scene->mRootNode, scene, root_transform); |
|
|
|
|
std::vector<Mesh> meshes; |
|
|
|
|
texture_cache textures; |
|
|
|
|
|
|
|
|
|
glCheckError(); |
|
|
|
|
process_node(meshes, scene->mRootNode, scene, root_transform, textures, fs, |
|
|
|
|
path.directory()); |
|
|
|
|
|
|
|
|
|
std::vector<texture> texture_v; |
|
|
|
|
for (auto [k, v] : textures) { |
|
|
|
|
texture_v.push_back(v); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
model m{meshes, std::string(path.directory().str()), root_transform}; |
|
|
|
|
return m; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
auto dotrans(auto matrix) { |
|
|
|
@ -289,6 +312,93 @@ void print_mat(glm::mat4 x) {
@@ -289,6 +312,93 @@ void print_mat(glm::mat4 x) {
|
|
|
|
|
spdlog::info(""); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Mesh model_loader::process_mesh(texture_cache textures_loaded, aiMesh *mesh, |
|
|
|
|
const aiScene *scene, glm::mat4 transform_accum, |
|
|
|
|
vfs::fsmount &fs, |
|
|
|
|
vfs::zpath_view mat_directory) { |
|
|
|
|
std::vector<vertex> vertices; |
|
|
|
|
std::vector<unsigned int> indices; |
|
|
|
|
std::vector<texture> textures; |
|
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < mesh->mNumVertices; i++) { |
|
|
|
|
glm::vec3 pos = glm::vec3(mesh->mVertices[i].x, mesh->mVertices[i].y, |
|
|
|
|
mesh->mVertices[i].z); |
|
|
|
|
glm::vec3 norm = glm::vec3(mesh->mNormals[i].x, mesh->mNormals[i].y, |
|
|
|
|
mesh->mNormals[i].z); |
|
|
|
|
glm::vec3 tangent = glm::vec3(mesh->mTangents[i].x, mesh->mTangents[i].y, |
|
|
|
|
mesh->mTangents[i].z); |
|
|
|
|
|
|
|
|
|
vertex v; |
|
|
|
|
v.position = pos; |
|
|
|
|
v.normal = norm; |
|
|
|
|
v.tangent = tangent; |
|
|
|
|
|
|
|
|
|
if (mesh->mTextureCoords[0]) // does the mesh contain texture coordinates?
|
|
|
|
|
{ |
|
|
|
|
glm::vec2 vec; |
|
|
|
|
vec.x = mesh->mTextureCoords[0][i].x; |
|
|
|
|
vec.y = mesh->mTextureCoords[0][i].y; |
|
|
|
|
v.texture_coords = vec; |
|
|
|
|
} else { |
|
|
|
|
v.texture_coords = glm::vec2(0.0f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
vertices.push_back(v); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* https://assimp-docs.readthedocs.io/en/latest/usage/use_the_lib.html#ai-mat-tex
|
|
|
|
|
* Each mesh is associated with a material. |
|
|
|
|
* The material stores properties as well as textures. |
|
|
|
|
*/ |
|
|
|
|
if (mesh->mMaterialIndex >= 0) { |
|
|
|
|
aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex]; |
|
|
|
|
|
|
|
|
|
std::vector<texture> diffuseMaps = load_material_textures( |
|
|
|
|
textures_loaded, scene, material, aiTextureType_DIFFUSE, |
|
|
|
|
"texture_diffuse", fs, mat_directory); |
|
|
|
|
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); |
|
|
|
|
|
|
|
|
|
std::vector<texture> specularMaps = load_material_textures( |
|
|
|
|
textures_loaded, scene, material, aiTextureType_SPECULAR, |
|
|
|
|
"texture_specular", fs, mat_directory); |
|
|
|
|
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); |
|
|
|
|
|
|
|
|
|
std::vector<texture> normalMaps = load_material_textures( |
|
|
|
|
textures_loaded, scene, material, aiTextureType_NORMALS, |
|
|
|
|
"texture_normal", fs, mat_directory); |
|
|
|
|
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
glCheckError(); |
|
|
|
|
|
|
|
|
|
return Mesh(vertices, indices, textures, transform_accum); |
|
|
|
|
} |
|
|
|
|
void model_loader::process_node(std::vector<Mesh> &meshes, aiNode *node, |
|
|
|
|
const aiScene *scene, glm::mat4 transform_accum, |
|
|
|
|
texture_cache textures_loaded, vfs::fsmount &fs, |
|
|
|
|
vfs::zpath_view mat_directory) { |
|
|
|
|
auto next_transform = transform_accum * mat4from_aimat(node->mTransformation); |
|
|
|
|
spdlog::info("Got child node {}", node->mName.C_Str()); |
|
|
|
|
for (unsigned int i = 0; i < node->mNumMeshes; i++) { |
|
|
|
|
aiMesh *mesh = scene->mMeshes[node->mMeshes[i]]; |
|
|
|
|
meshes.push_back(process_mesh(textures_loaded, mesh, scene, next_transform, |
|
|
|
|
fs, mat_directory)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// recurse
|
|
|
|
|
for (unsigned int i = 0; i < node->mNumChildren; i++) { |
|
|
|
|
process_node(meshes, node->mChildren[i], scene, next_transform, |
|
|
|
|
textures_loaded, fs, mat_directory); |
|
|
|
|
} |
|
|
|
|
glCheckError(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Model::process_node(aiNode *node, const aiScene *scene, |
|
|
|
|
glm::mat4 transform_accum) { |
|
|
|
|
auto next_transform = transform_accum * mat4from_aimat(node->mTransformation); |
|
|
|
@ -442,6 +552,69 @@ unsigned int Model::texture_from_file(const char *fname,
@@ -442,6 +552,69 @@ unsigned int Model::texture_from_file(const char *fname,
|
|
|
|
|
return bind_texture_from_stb(data, width, height, nrComponents); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
unsigned int model_loader::texture_from_file(vfs::fsmount &fs, |
|
|
|
|
vfs::zpath_view path) { |
|
|
|
|
|
|
|
|
|
int width, height, nrComponents; |
|
|
|
|
|
|
|
|
|
auto file_data = |
|
|
|
|
fs.open_read(path) |
|
|
|
|
.and_then([](auto a) -> auto { return a->read(); }) |
|
|
|
|
.or_else([](error a) -> auto { throw std::runtime_error(a.message); }) |
|
|
|
|
.value(); |
|
|
|
|
|
|
|
|
|
stbi_uc *data = stbi_load_from_memory( |
|
|
|
|
reinterpret_cast<stbi_uc *>(file_data.data()), file_data.size(), &width, |
|
|
|
|
&height, &nrComponents, 4); |
|
|
|
|
|
|
|
|
|
return bind_texture_from_stb(data, width, height, nrComponents); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::vector<texture> model_loader::load_material_textures( |
|
|
|
|
std::unordered_map<std::string, texture> textures_loaded, |
|
|
|
|
const aiScene *scene, aiMaterial *mat, aiTextureType type, |
|
|
|
|
std::string typeName, vfs::fsmount &fs, vfs::zpath_view mat_directory) { |
|
|
|
|
std::vector<texture> textures; |
|
|
|
|
for (unsigned int i = 0; i < mat->GetTextureCount(type); i++) { |
|
|
|
|
aiString tex_filename; |
|
|
|
|
mat->GetTexture(type, i, &tex_filename); |
|
|
|
|
bool skip = false; |
|
|
|
|
|
|
|
|
|
// checking if already loaded
|
|
|
|
|
if (textures_loaded.contains(std::string(tex_filename.C_Str()))) { |
|
|
|
|
textures.push_back(textures_loaded.at(std::string(tex_filename.C_Str()))); |
|
|
|
|
spdlog::info("Already loaded texture {}", tex_filename.C_Str()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!skip) { |
|
|
|
|
texture texture; |
|
|
|
|
if (auto embedded_texture = |
|
|
|
|
scene->GetEmbeddedTexture(tex_filename.C_Str())) { |
|
|
|
|
// embedded texture
|
|
|
|
|
|
|
|
|
|
spdlog::info("Loading embedded texture"); |
|
|
|
|
texture.id = load_embedded_texture(embedded_texture); |
|
|
|
|
texture.type = type; |
|
|
|
|
texture.type_str = typeName; |
|
|
|
|
textures.push_back(texture); |
|
|
|
|
textures_loaded.insert({std::string(tex_filename.C_Str()), texture}); |
|
|
|
|
} else { |
|
|
|
|
spdlog::info("Loading texture from file {}", tex_filename.C_Str()); |
|
|
|
|
texture.id = model_loader::texture_from_file( |
|
|
|
|
fs, vfs::zpath_view(tex_filename.C_Str())); |
|
|
|
|
texture.type = type; |
|
|
|
|
texture.type_str = typeName; |
|
|
|
|
texture.path = tex_filename.C_Str(); |
|
|
|
|
textures.push_back(texture); |
|
|
|
|
textures_loaded.insert({std::string(tex_filename.C_Str()), texture}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
glCheckError(); |
|
|
|
|
return textures; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::vector<texture> Model::load_material_textures(const aiScene *scene, |
|
|
|
|
aiMaterial *mat, |
|
|
|
|
aiTextureType type, |
|
|
|
|