Optimize model texture loading, test assimp with complicated model

This commit is contained in:
SeanOMik 2022-09-26 23:43:08 -04:00
parent ab3f6c98ff
commit 475bb08f0e
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
10 changed files with 108 additions and 86 deletions

View File

@ -1,5 +1,2 @@
*.blend *
*.obj !shaders
*.png
*.bin
*.gltf

View File

@ -1,4 +1,3 @@
#include "entt/entity/fwd.hpp"
#include "simpleengine/camera.h" #include "simpleengine/camera.h"
#include "simpleengine/ecs/component/mesh_component.h" #include "simpleengine/ecs/component/mesh_component.h"
#include <simpleengine/ecs/component/model_component.h> #include <simpleengine/ecs/component/model_component.h>
@ -12,7 +11,6 @@
#include "simpleengine/gfx/renderer.h" #include "simpleengine/gfx/renderer.h"
#include "simpleengine/gfx/texture.h" #include "simpleengine/gfx/texture.h"
#include "simpleengine/vector.h" #include "simpleengine/vector.h"
#include <GLFW/glfw3.h>
#include <simpleengine/gfx/shader.h> #include <simpleengine/gfx/shader.h>
#include <simpleengine/renderable.h> #include <simpleengine/renderable.h>
#include <simpleengine/event/event.h> #include <simpleengine/event/event.h>
@ -21,11 +19,8 @@
#include <simpleengine/vertex.h> #include <simpleengine/vertex.h>
#include <simpleengine/gfx/shaders/core_3d_shader.h> #include <simpleengine/gfx/shaders/core_3d_shader.h>
#include <simpleengine/gfx/model.h> #include <simpleengine/gfx/model.h>
#include <simpleengine/scene.h> #include <simpleengine/scene.h>
//#include <simpleengine/scene.h>
#include <glm/ext/matrix_clip_space.hpp> #include <glm/ext/matrix_clip_space.hpp>
#include <glm/fwd.hpp> #include <glm/fwd.hpp>
#include <assimp/material.h> #include <assimp/material.h>
@ -170,8 +165,8 @@ int main(int argc, char *argv[]) {
5, 6, 12, 12, 6, 13 5, 6, 12, 12, 6, 13
}; };
std::unordered_map<aiTextureType, std::vector<se::gfx::Texture>> textures; std::unordered_map<aiTextureType, std::vector<std::shared_ptr<se::gfx::Texture>>> textures;
textures.emplace(white_texture.type, std::vector<se::gfx::Texture>{ white_texture }); textures.emplace(white_texture.type, std::vector<std::shared_ptr<se::gfx::Texture>>{ std::make_shared<se::gfx::Texture>(white_texture) });
se::gfx::Material white_material(textures, 1.f, 0.f, 0.f, 0.f, 0.f); se::gfx::Material white_material(textures, 1.f, 0.f, 0.f, 0.f, 0.f);
// Create a renderer // Create a renderer
@ -184,10 +179,17 @@ int main(int argc, char *argv[]) {
// Create an Entity in the Scene and add components to it. // Create an Entity in the Scene and add components to it.
se::ecs::Entity entity = scene->create_entity(); se::ecs::Entity entity = scene->create_entity();
entity.add_component<se::ModelComponent>("examples/dev_testing/resources/dragon.obj"); //entity.add_component<se::ModelComponent>("examples/dev_testing/resources/dragon.obj");
entity.add_component<se::RotatingComponent>(); //entity.add_component<se::ModelComponent>("examples/dev_testing/resources/stall.obj");
entity.add_component<se::ModelComponent>("examples/dev_testing/resources/backpack/backpack.obj");
//entity.add_component<se::ModelComponent>("examples/dev_testing/resources/scientist/scientist.fbx");
//entity.add_component<se::ModelComponent>("examples/dev_testing/resources/paradigm/paradigm.fbx");
//entity.add_component<se::RotatingComponent>();
auto& transform_comp = entity.add_component<se::TransformComponent>(); auto& transform_comp = entity.add_component<se::TransformComponent>();
transform_comp.translate(12.f, -4.f, 0.f); transform_comp.translate(15.f, -8.f, 0.f);
/* transform_comp.scale(0.1f);
transform_comp.rotate_z(-90.f);*/
transform_comp.rotate_x(-90.f);
// Create the entity and add the model component to it. // Create the entity and add the model component to it.

View File

@ -3,12 +3,14 @@
#include "texture.h" #include "texture.h"
#include <assimp/material.h> #include <assimp/material.h>
#include <unordered_map> #include <unordered_map>
#include <memory>
namespace simpleengine::gfx { namespace simpleengine::gfx {
class Material { class Material {
public: public:
std::unordered_map<aiTextureType, std::vector<Texture>> textures; std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> textures;
float ambient_scalar; float ambient_scalar;
float diffuse_scalar; float diffuse_scalar;
@ -16,7 +18,7 @@ namespace simpleengine::gfx {
float shine; float shine;
float reflectivity; float reflectivity;
Material(std::unordered_map<aiTextureType, std::vector<Texture>> textures, float shine = 1.f, float reflectivity = 0.f, float specular_scalar = 0.f, float ambient_scalar = 0.f, float diffuse_scalar = 0.f) : Material(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> textures, float shine = 1.f, float reflectivity = 0.f, float specular_scalar = 0.f, float ambient_scalar = 0.f, float diffuse_scalar = 0.f) :
textures(textures), ambient_scalar(ambient_scalar), diffuse_scalar(diffuse_scalar), specular_scalar(specular_scalar), textures(textures), ambient_scalar(ambient_scalar), diffuse_scalar(diffuse_scalar), specular_scalar(specular_scalar),
shine(shine), reflectivity(reflectivity) { shine(shine), reflectivity(reflectivity) {

View File

@ -26,8 +26,10 @@ namespace simpleengine::gfx {
Model(std::string file_path); Model(std::string file_path);
void load_model(std::string path); void load_model(std::string path);
void process_node(aiNode* node, const aiScene* scene); void process_node(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiNode* node, const aiScene* scene);
gfx::Mesh process_mesh(aiMesh* mesh, const aiScene* scene); gfx::Mesh process_mesh(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiMesh* mesh, const aiScene* scene);
std::vector<Texture> load_material_textures(aiMaterial* material, aiTextureType type);
std::unordered_map<aiTextureType, std::vector<Texture>> load_all_textures(aiMaterial* material);
std::vector<std::shared_ptr<Texture>> load_material_texture(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiMaterial* material, aiTextureType type);
}; };
} }

View File

@ -30,6 +30,7 @@ namespace simpleengine::gfx {
int width; int width;
int channels; int channels;
aiTextureType type; aiTextureType type;
std::string path;
/** /**
* @brief Construct a new Texture object from a path. * @brief Construct a new Texture object from a path.
@ -38,7 +39,7 @@ namespace simpleengine::gfx {
* @param img_2d Whether or not the texture is 2D. * @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture. * @param mipmap Whether or not to generate mipmaps for this texture.
*/ */
Texture(const char* path, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, bool img_2d = true, bool mipmap = true); Texture(const char* path, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, bool img_2d = true, bool flip_vertically = false, bool mipmap = true);
/** /**
* @brief Construct a new Texture object from the loaded file buffer. * @brief Construct a new Texture object from the loaded file buffer.
@ -48,7 +49,7 @@ namespace simpleengine::gfx {
* @param img_2d Whether or not the texture is 2D. * @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture. * @param mipmap Whether or not to generate mipmaps for this texture.
*/ */
Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, bool img_2d = true, bool mipmap = true); Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, bool img_2d = true, bool flip_vertically = false, bool mipmap = true);
/** /**
* @brief Construct a new Texture object from the loaded file buffer. * @brief Construct a new Texture object from the loaded file buffer.
@ -57,7 +58,7 @@ namespace simpleengine::gfx {
* @param img_2d Whether or not the texture is 2D. * @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture. * @param mipmap Whether or not to generate mipmaps for this texture.
*/ */
Texture(std::vector<unsigned char> buffer, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, bool img_2d = true, bool mipmap = true); Texture(std::vector<unsigned char> buffer, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, bool img_2d = true, bool flip_vertically = false, bool mipmap = true);
static Texture white_texture(); static Texture white_texture();

View File

@ -12,6 +12,5 @@ pkgs.mkShell {
glfw glfw
glm glm
assimp assimp
(callPackage ./soil2.nix { })
]; ];
} }

View File

@ -1,37 +0,0 @@
{ lib
, stdenv
, fetchFromGitHub
, cmake
, libGL
, libX11
}:
stdenv.mkDerivation rec {
version = "39028e64921c03cabbc53b937da4a85aba264e00";
pname = "soil2";
src = fetchFromGitHub {
owner = "SpartanJ";
repo = pname;
rev = version;
sha256 = "sha256-zQQ8lwOkMCxdlf6zfnIOYVUTGVqnJuHL/LL8fbzxwHY=";
};
nativeBuildInputs = [ cmake libGL libX11 ];
installPhase = ''
runHook preInstall
cmake --build . --target install --config Release
runHook postInstall
'';
meta = with lib; {
description = "SOIL2 is a tiny C library used primarily for uploading textures into OpenGL.";
homepage = "https://github.com/SpartanJ/SOIL2";
license = licenses.mit0;
platforms = platforms.unix;
maintainers = with maintainers; [ seanomik ];
};
}

View File

@ -31,28 +31,28 @@ namespace simpleengine::gfx {
model_directory = path.substr(0, path.find_last_of('/')); model_directory = path.substr(0, path.find_last_of('/'));
process_node(scene->mRootNode, scene); std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> processed_textures;
process_node(processed_textures, scene->mRootNode, scene);
} }
void Model::process_node(aiNode* node, const aiScene* scene) { void Model::process_node(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiNode* node, const aiScene* scene) {
// process all the node's meshes (if any) // process all the node's meshes (if any)
for (int i = 0; i < node->mNumMeshes; i++) { for (int i = 0; i < node->mNumMeshes; i++) {
aiMesh *mesh = scene->mMeshes[node->mMeshes[i]]; aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(process_mesh(mesh, scene)); meshes.push_back(process_mesh(processed_textures, mesh, scene));
} }
// then do the same for each of its children // then do the same for each of its children
for (int i = 0; i < node->mNumChildren; i++) { for (int i = 0; i < node->mNumChildren; i++) {
process_node(node->mChildren[i], scene); process_node(processed_textures, node->mChildren[i], scene);
} }
} }
gfx::Mesh Model::process_mesh(aiMesh* mesh, const aiScene* scene) { gfx::Mesh Model::process_mesh(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiMesh* mesh, const aiScene* scene) {
std::vector<LitVertex> vertices; std::vector<LitVertex> vertices;
std::vector<unsigned int> indices; std::vector<unsigned int> indices;
std::vector<Texture> textures;
for(unsigned int i = 0; i < mesh->mNumVertices; i++) { for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
LitVertex vertex; LitVertex vertex;
vertex.color = glm::vec3(1.f); vertex.color = glm::vec3(1.f);
vertex.texture_id = 0; vertex.texture_id = 0;
@ -63,7 +63,7 @@ namespace simpleengine::gfx {
glm::vec3 normal(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z); glm::vec3 normal(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);
vertex.normal = normal; vertex.normal = normal;
if(mesh->mTextureCoords[0]) { if (mesh->mTextureCoords[0]) {
glm::vec2 tex_coord(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y); glm::vec2 tex_coord(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y);
vertex.tex_coord = tex_coord; vertex.tex_coord = tex_coord;
@ -83,18 +83,18 @@ namespace simpleengine::gfx {
// Create a default material and white texture. // Create a default material and white texture.
auto white_texture = gfx::Texture::white_texture(); auto white_texture = gfx::Texture::white_texture();
std::unordered_map<aiTextureType, std::vector<Texture>> default_textures; std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> default_textures;
default_textures.emplace(white_texture.type, std::vector<Texture>{ white_texture }); default_textures.emplace(white_texture.type, std::vector<std::shared_ptr<Texture>>{ std::make_shared<Texture>(white_texture) });
gfx::Material mat(default_textures, 1.f, 0.f, 0.f, 0.f, 0.f); gfx::Material mat(default_textures, 1.f, 0.f, 0.f, 0.f, 0.f);
if(mesh->mMaterialIndex >= 0) { if (mesh->mMaterialIndex >= 0) {
std::cout << "TODO: Process model materials!" << std::endl; std::cout << "TODO: Process model materials!" << std::endl;
std::unordered_map<aiTextureType, std::vector<Texture>> textures; std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> textures;
aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex]; aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
// Load Diffuse texture maps // Load Diffuse texture maps
std::vector<Texture> diffuse_maps = load_material_textures(material, aiTextureType_DIFFUSE); std::vector<std::shared_ptr<Texture>> diffuse_maps = load_material_texture(processed_textures, material, aiTextureType_DIFFUSE);
if (!diffuse_maps.empty()) textures.emplace(aiTextureType_DIFFUSE, diffuse_maps); if (!diffuse_maps.empty()) textures.emplace(aiTextureType_DIFFUSE, diffuse_maps);
// TODO Handle other types of texture maps // TODO Handle other types of texture maps
@ -102,25 +102,73 @@ namespace simpleengine::gfx {
if (!textures.empty()) { if (!textures.empty()) {
// TODO: Find a way to let the user set the scalars. // TODO: Find a way to let the user set the scalars.
mat = Material(textures, 1.f, 0.f, 0.f, 0.f, 0.f); mat = Material(textures, 1.f, 0.f, 0.f, 0.f, 0.f);
// Add `textures` into the `processed_textures` list.
for (const auto& pair : textures) {
for (const auto& texture : pair.second) {
bool contains = false;
auto found = processed_textures.find(pair.first);
if (found != processed_textures.end()) {
for (const auto& processed_text : found->second) {
if (processed_text->path == texture->path) {
contains = true;
break;
}
}
if (!contains) {
//found->second
found->second.emplace_back(texture);
}
} else {
processed_textures.emplace(pair.first, std::vector<std::shared_ptr<Texture>>{ pair.second });
}
}
}
} }
} }
return Mesh(vertices, indices, mat); return Mesh(vertices, indices, mat);
} }
std::vector<Texture> Model::load_material_textures(aiMaterial* material, aiTextureType type) { std::unordered_map<aiTextureType, std::vector<Texture>> load_all_textures(aiMaterial* material) {
std::vector<Texture> textures; // Load Diffuse texture maps
return {};
}
for(int i = 0; i < material->GetTextureCount(type); i++) { std::vector<std::shared_ptr<Texture>> Model::load_material_texture(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiMaterial* material, aiTextureType type) {
aiString texture_path; std::vector<std::shared_ptr<Texture>> textures;
material->GetTexture(type, i, &texture_path);
for (int i = 0; i < material->GetTextureCount(type); i++) {
aiString texture_path_ai;
material->GetTexture(type, i, &texture_path_ai);
std::string texture_path = texture_path_ai.C_Str();
// If the texture has been read, we should skip it.
bool skip = false;
for (const auto& pair : processed_textures) {
if (pair.first == type) {
for (const auto& texture : pair.second) {
if (texture->path == texture_path) {
textures.emplace_back(texture); // Push a copy of the texture for this Mesh.
skip = true;
break;
}
}
}
}
if (skip) continue;
std::stringstream ss; std::stringstream ss;
ss << model_directory << "/" << texture_path.C_Str(); ss << model_directory << "/" << texture_path;
std::string full_path = ss.str(); std::string full_path = ss.str();
Texture texture(full_path.c_str(), type); Texture texture(full_path.c_str(), type, true, true);
textures.emplace_back(texture); texture.path = texture_path;
textures.emplace_back(std::make_shared<Texture>(texture));
std::cout << "Texture full path: " << full_path << ", texture_path: " << texture_path << std::endl;
} }
return textures; return textures;

View File

@ -111,7 +111,7 @@ namespace simpleengine::gfx {
if (texture_count >= 16) break; if (texture_count >= 16) break;
glActiveTexture(GL_TEXTURE0 + texture_count); glActiveTexture(GL_TEXTURE0 + texture_count);
glBindTextureUnit(texture_count, texture.get_texture_id()); glBindTextureUnit(texture_count, texture->get_texture_id());
texture_count++; texture_count++;
} }

View File

@ -4,7 +4,7 @@
#include <stb_image.h> #include <stb_image.h>
namespace simpleengine::gfx { namespace simpleengine::gfx {
Texture::Texture(const char* path, aiTextureType type, bool img_2d, bool mipmap): type(type) { Texture::Texture(const char* path, aiTextureType type, bool img_2d, bool flip_vertically, bool mipmap): type(type) {
image_type_gl = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D; image_type_gl = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D;
glGenTextures(1, &texture_id); glGenTextures(1, &texture_id);
@ -17,6 +17,8 @@ namespace simpleengine::gfx {
glTexParameteri(image_type_gl, GL_TEXTURE_MIN_FILTER, linear_param); glTexParameteri(image_type_gl, GL_TEXTURE_MIN_FILTER, linear_param);
glTexParameteri(image_type_gl, GL_TEXTURE_MAG_FILTER, linear_param); glTexParameteri(image_type_gl, GL_TEXTURE_MAG_FILTER, linear_param);
stbi_set_flip_vertically_on_load(flip_vertically);
// Read 4 channels (RGBA) // Read 4 channels (RGBA)
img_data = stbi_load(path, &width, &height, &channels, 4); img_data = stbi_load(path, &width, &height, &channels, 4);
if(!img_data) { if(!img_data) {
@ -32,10 +34,12 @@ namespace simpleengine::gfx {
glGenerateMipmap(image_type_gl); glGenerateMipmap(image_type_gl);
} }
stbi_set_flip_vertically_on_load(false);
unbind(); unbind();
} }
Texture::Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type, bool img_2d, bool mipmap): type(type) { Texture::Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type, bool img_2d, bool flip_vertically, bool mipmap): type(type) {
image_type_gl = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D; image_type_gl = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D;
glGenTextures(1, &texture_id); glGenTextures(1, &texture_id);
@ -48,6 +52,8 @@ namespace simpleengine::gfx {
glTexParameteri(image_type_gl, GL_TEXTURE_MIN_FILTER, linear_param); glTexParameteri(image_type_gl, GL_TEXTURE_MIN_FILTER, linear_param);
glTexParameteri(image_type_gl, GL_TEXTURE_MAG_FILTER, linear_param); glTexParameteri(image_type_gl, GL_TEXTURE_MAG_FILTER, linear_param);
stbi_set_flip_vertically_on_load(flip_vertically);
// Read 4 channels (RGBA) // Read 4 channels (RGBA)
img_data = stbi_load_from_memory(buffer, buffer_length, &width, &height, &channels, 4); img_data = stbi_load_from_memory(buffer, buffer_length, &width, &height, &channels, 4);
if(!img_data) { if(!img_data) {
@ -63,10 +69,12 @@ namespace simpleengine::gfx {
glGenerateMipmap(image_type_gl); glGenerateMipmap(image_type_gl);
} }
stbi_set_flip_vertically_on_load(false);
unbind(); unbind();
} }
Texture::Texture(std::vector<unsigned char> buffer, aiTextureType type, bool img_2d, bool mipmap) : Texture::Texture(std::vector<unsigned char> buffer, aiTextureType type, bool img_2d, bool flip_vertically, bool mipmap) :
Texture(buffer.data(), buffer.size(), type, img_2d, mipmap) { Texture(buffer.data(), buffer.size(), type, img_2d, mipmap) {
} }