341 lines
7.5 KiB
C++
341 lines
7.5 KiB
C++
|
|
#include <GL/glew.h>
|
|
#include <GLFW/glfw3.h>
|
|
|
|
#include <glm/matrix.hpp>
|
|
|
|
#include <unordered_map>
|
|
#include <iostream>
|
|
#include <vector>
|
|
|
|
#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<Arrays::Vertex> vertices;
|
|
std::vector<unsigned int> indices;
|
|
std::vector<glm::mat4> transforms;
|
|
std::unordered_map<const aiTexture*, unsigned int> 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<glm::vec<4, unsigned char>> 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));
|
|
}
|
|
|