#include #include #include #include #include #include #include "mesh.hpp" #include "arrays.hpp" #include "texture.hpp" #include "model.hpp" #include "../../util/streams.hpp" using namespace Sim::Graphics::Data; struct ProcState { unsigned int offset = 0; std::string base; std::vector vertices; std::vector indices; std::vector transforms; std::unordered_map handles; }; static unsigned int proc_texture(const ProcState& state, aiMaterial* mat, const aiScene* scene, aiTextureType type, int index) { aiString str; mat->GetTexture(type, index, &str); if(str.C_Str()[0] == '\0') { return 0; } const aiTexture* tex = scene->GetEmbeddedTexture(str.C_Str()); if(tex != nullptr) { unsigned int handle = state.handles.find(tex)->second; std::cout << "Using preloaded texture: " << tex->mFilename.C_Str() << "\n"; return handle; } std::string filename(str.C_Str()); std::replace(filename.begin(), filename.end(), '\\', '/'); return Texture::load(state.base + "/" + filename); } static void proc_mesh(ProcState& state, aiMesh* mesh, const aiScene* scene) { aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex]; aiString name; material->Get(AI_MATKEY_NAME, name); /* std::cout << "Material " << name.C_Str() << " has " << material->mNumProperties << " properties\n"; for(int i = 0; i < material->mNumProperties; i++) { aiMaterialProperty* prop = material->mProperties[i]; std::cout << " " << prop->mKey.C_Str() << ": type=" << prop->mType << " index=" << prop->mIndex << " length=" << prop->mDataLength; if(prop->mType == 1) { float* v = (float*)prop->mData; std::cout << " value="; switch(prop->mDataLength) { case 4: std::cout << v[0]; break; case 8: std::cout << "{" << v[0] << ", " << v[1] << "}"; break; case 12: std::cout << "{" << v[0] << ", " << v[1] << ", " << v[2] << "}"; break; case 16: std::cout << "{" << v[0] << ", " << v[1] << ", " << v[2] << ", " << v[3] << "}"; break; default: std::cout << "unknown"; } } std::cout << "\n"; }*/ glm::vec3 matv(0); aiColor4D ai_cb; aiColor4D ai_em; material->Get(AI_MATKEY_ROUGHNESS_FACTOR, matv[0]); material->Get(AI_MATKEY_METALLIC_FACTOR, matv[1]); material->Get(AI_MATKEY_EMISSIVE_INTENSITY, matv[2]); material->Get(AI_MATKEY_COLOR_EMISSIVE, ai_em); material->Get(AI_MATKEY_BASE_COLOR, ai_cb); glm::vec4 cb = {ai_cb[0], ai_cb[1], ai_cb[2], ai_cb[3]}; glm::vec4 em = {ai_em[0], ai_em[1], ai_em[2], ai_em[3]}; if(em.x > 0 || em.y > 0 || em.z > 0) { cb = em; } unsigned int handle = proc_texture(state, material, scene, aiTextureType_BASE_COLOR, 0); unsigned int offset = state.offset; if(!handle) { handle = proc_texture(state, material, scene, aiTextureType_DIFFUSE, 0); } if(!handle) { handle = Texture::handle_white; } for(unsigned int i = 0; i < mesh->mNumVertices; i++) { Arrays::Vertex vertex; auto [x, y, z] = mesh->mVertices[i]; vertex.pos = glm::vec3(x, y, z); vertex.transform_id = state.transforms.size(); vertex.material = matv; vertex.texid = handle; vertex.colour = cb; if(mesh->HasNormals()) { auto [x, y, z] = mesh->mNormals[i]; vertex.normal = glm::vec3(x, y, z); } /*if(mesh->HasTangentsAndBitangents()) { auto [x1, y1, z1] = mesh->mTangents[i]; auto [x2, y2, z2] = mesh->mBitangents[i]; vertex.tangent = {x1, y1, z1}; vertex.bitangent = {x2, y2, z2}; }*/ if(mesh->mTextureCoords[0]) { auto [x, y, z] = mesh->mTextureCoords[0][i]; vertex.texpos = {x, y}; } state.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++) { state.indices.push_back(face.mIndices[j] + offset); } } state.offset += mesh->mNumVertices; } glm::mat4 convert_mat(aiMatrix4x4 m) { return { m.a1, m.b1, m.c1, m.d1, m.a2, m.b2, m.c2, m.d2, m.a3, m.b3, m.c3, m.d3, m.a4, m.b4, m.c4, m.d4 }; } bool starts_with(const char* base, const char* check) { while(base[0] != '\0' && check[0] != '\0') { if(base[0] != check[0]) { return false; } base++; check++; } return (check[0] == '\0'); } static void proc_node(ProcState& state, glm::mat4 mat, aiNode* node, const aiScene* scene, const char* search) { mat = convert_mat(node->mTransformation) * mat; if(starts_with(node->mName.C_Str(), search)) { for(size_t i = 0; i < node->mNumMeshes; i++) { aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; proc_mesh(state, mesh, scene); } if(node->mNumMeshes > 0) { state.transforms.push_back(mat); } } for(size_t i = 0; i < node->mNumChildren; i++) { proc_node(state, mat, node->mChildren[i], scene, search); } } static unsigned int proc_embedded_texture(aiTexture* tex) { std::cout << "Loading embedded data: " << tex->mFilename.C_Str() << "\n"; if(tex->mHeight == 0) { return Texture::load_mem((unsigned char*)tex->pcData, tex->mWidth); } std::vector> pixels; pixels.reserve(tex->mWidth * tex->mHeight); // convert image to get RGBA for(int i = 0; i < tex->mWidth * tex->mHeight; i++) { aiTexel t = tex->pcData[i]; pixels.push_back({t.r, t.g, t.b, t.a}); } return Texture::load_mem(&pixels[0][0], tex->mWidth, tex->mHeight, 4); } glm::mat4 get_transforms(const aiNode* node) { glm::mat4 mat(1); while(node->mParent != nullptr) { mat = mat * convert_mat(node->mTransformation); node = node->mParent; } return mat; } glm::mat4 Model::load_matrix(const char* name) const { return get_transforms(scene->mRootNode->FindNode(name)); } Model::Model(std::string base, std::string filename) : base(base) { std::string path = base + "/" + filename; scene = importer.ReadFile(path.c_str(), aiProcess_Triangulate | aiProcess_FlipUVs); textures.reserve(scene->mNumTextures); for(int i = 0; i < scene->mNumTextures; i++) { aiTexture* tex = scene->mTextures[i]; unsigned int handle = proc_embedded_texture(tex); textures.push_back(handle); } for(int i = 0; i < scene->mNumLights; i++) { aiLight* light = scene->mLights[i]; glm::mat4 mat = load_matrix(light->mName.C_Str()); auto [x, y, z] = light->mPosition; auto [r, g, b] = light->mColorDiffuse; glm::vec4 pos = mat * glm::vec4(x, y, z, 1); lights.push_back({ glm::vec3(pos), {r, g, b}, }); } for(int i = 0; i < scene->mNumCameras; i++) { aiCamera* camera = scene->mCameras[i]; glm::mat4 mat = load_matrix(camera->mName.C_Str()); auto [x, y, z] = camera->mPosition; auto [dx, dy, dz] = camera->mLookAt; auto [ux, uy, uz] = camera->mUp; glm::vec4 pos = mat * glm::vec4(x, y, z, 1); glm::vec3 look = glm::normalize(glm::mat3(mat) * glm::vec3(dx, dy, dz)); glm::vec3 up = glm::normalize(glm::mat3(mat) * glm::vec3(ux, uy, uz)); cameras.push_back({.pos=glm::vec3(pos), .look=look, .up=up, .fov=camera->mHorizontalFOV}); } } Mesh Model::load(const char* name, glm::mat4 mat) const { Mesh mesh; ProcState state {.base = base}; proc_node(state, mat, scene->mRootNode, scene, name); mesh.vertices = std::move(state.vertices); mesh.indices = std::move(state.indices); mesh.transforms = std::move(state.transforms); return mesh; } Mesh Model::load_root(glm::mat4 mat) const { return load("", mat); } Mesh Model::load(const char* name) const { return load(name, glm::mat4(1)); } Mesh Model::load_root() const { return load("", glm::mat4(1)); }