Skip to content

Instantly share code, notes, and snippets.

@iKlsR
Created August 12, 2012 17:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iKlsR/3333055 to your computer and use it in GitHub Desktop.
Save iKlsR/3333055 to your computer and use it in GitHub Desktop.
hrrmm code from irc
#include "model.hpp"
#include "global.hpp"
#include <sys/stat.h>
#include <glm/gtx/integer.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <sstream>
#include <vector>
#include <map>
Model::Model():
normal_offset(0),
texture_offset(0),
tangent_offset(0)
{}
Model::Model(Model&& m) :
vao(std::move(m.vao)),
vbo(std::move(m.vbo)),
ibo(std::move(m.ibo)),
normal_offset(m.normal_offset),
texture_offset(m.texture_offset),
tangent_offset(m.tangent_offset),
meshes(std::move(m.meshes)),
animations(std::move(m.animations)),
materials(std::move(m.materials)),
scenes(std::move(m.scenes)),
bounding_boxes(std::move(m.bounding_boxes)),
bounding_box_vbo(std::move(m.bounding_box_vbo)),
bounding_box_vao(std::move(m.bounding_box_vao))
{
}
Model& Model::operator=(Model &&m)
{
vao = std::move(m.vao);
vbo = std::move(m.vbo);
ibo = std::move(m.ibo);
normal_offset = m.normal_offset;
texture_offset = m.texture_offset;
tangent_offset = m.tangent_offset;
meshes = std::move(m.meshes);
animations = std::move(m.animations);
materials = std::move(m.materials);
scenes = std::move(m.scenes);
bounding_boxes = std::move(m.bounding_boxes);
bounding_box_vbo = std::move(m.bounding_box_vbo);
bounding_box_vao = std::move(m.bounding_box_vao);
hit_volume = std::move(m.hit_volume);
return *this;
}
//returns textures that are used by the model
std::vector<std::string> Model::initialize(const std::string& filename)
{
Assimp::Importer importer;
std::ifstream file(filename);
if(!file.is_open()){
std::cerr << "Model not found " << filename << std::endl;
assert(file.is_open());
return std::vector<std::string>();
}
file.close();
auto scene = importer.ReadFile(filename, aiProcessPreset_TargetRealtime_Quality);
if(scene == nullptr){
std::cerr << "Failed to load model " << filename << std::endl
<< importer.GetErrorString() << std::endl;
return std::vector<std::string>();
}
//if reloading fails after this, the model will break and the rest is UB
meshes.clear();
animations.clear();
materials.clear();
scenes.clear();
bounding_boxes.clear();
recursive_extract_scene_formation(scene->mRootNode,aiMatrix4x4(),std::vector<glm::mat4>());
load_meshes(*scene);
load_animations(*scene);
auto required_textures = load_materials(*scene);
remove_boundingboxes_from_scene_data();
vao.initialize();
vao.bind();
ibo.bind();
vbo.bind();
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glVertexAttribPointer(0,3,GL_FLOAT,false,0,nullptr); //Vertex
glVertexAttribPointer(1,3,GL_FLOAT,false,0,normal_offset); //Normal
glVertexAttribPointer(2,3,GL_FLOAT,false,0,texture_offset); //Texture
glVertexAttribPointer(3,3,GL_FLOAT,false,0,tangent_offset); //Tangent
glVertexAttribPointer(4,3,GL_FLOAT,false,0,bitangent_offset); //Tangent
//vao.unbind();
bounding_box_vao.initialize();
bounding_box_vao.bind();
bounding_box_vbo.bind();
glEnableVertexAttribArray(0);
glVertexAttribPointer(0,3,GL_FLOAT,false,0,nullptr);
bounding_box_vao.unbind();
return required_textures;
}
std::vector<Path> Model::load_path_only(const std::string& filename)
{
assert(filename.size() > 4);
std::string file_type = filename.substr(filename.size()-3);
assert(file_type == "obj");
auto pathes = fast_obj_path_load(filename);
return pathes;
}
Heightmap Model::load_heightmap_only(const std::string& filename)
{
assert(filename.size() > 4);
std::string file_type = filename.substr(filename.size()-3);
assert(file_type == "obj");
auto vertices = fast_obj_heightmap_load("models/"+filename);
std::sort(vertices.begin(),vertices.end(),[]
(const glm::vec3& a, const glm::vec3& b)
{
return int(a.z+0.5) < int(b.z+0.5);
});
std::stable_sort(vertices.begin(),vertices.end(),[]
(const glm::vec3& a, const glm::vec3& b)
{
return int(a.x+0.5) < int(b.x+0.5);
});
std::vector<float> vertices2;
vertices2.reserve(vertices.size());
for(auto iter = vertices.begin();iter != vertices.end();++iter)
vertices2.push_back(iter->y);
unsigned int height_samples_width = glm::sqrt(vertices2.size());
unsigned int height_samples_depth = height_samples_width;
assert(height_samples_width*height_samples_depth == vertices2.size());
BoundingBox bb = calculate_bounding_box(vertices.begin(),vertices.end(),glm::mat4());
float real_width = bb.max.x-bb.min.x;
float real_depth = bb.max.z-bb.min.z;
float width_scale = real_width/height_samples_width;
float depth_scale = real_depth/height_samples_depth;
return Heightmap(vertices2, height_samples_width, height_samples_depth, width_scale, depth_scale);
}
void Model::update_animations()
{
for(auto iter = animations.begin();iter != animations.end();++iter)
iter->update();
}
std::vector<BoundingBox> Model::get_bounding_boxes()const
{
return bounding_boxes;
}
HitVolume Model::get_hit_volume()const
{
return hit_volume;
}
void Model::render_bounding_boxes(const Shader& shader)const
{
shader["transformation"] = glm::mat4();
bounding_box_vao.bind();
int index=0;
for(auto iter = bounding_boxes.begin();iter != bounding_boxes.end();++iter){
//shader["transformation"] = iter->transformation;
glDrawArrays(GL_LINE_LOOP,index*24,19);
++index;
}
bounding_box_vao.unbind();
}
void Model::render(const Shader& shader, const TextureManager& texture_manager, const glm::mat4& transformation)const
{
vao.bind();
for(auto iter = scenes.begin();iter != scenes.end();++iter){
const Animation* animation = get_possible_animation(iter->name);
if(animation != nullptr){
glm::mat4 animation_transformation = animation->get_transformation();
glm::mat4 user_transformation = transformation;
shader["transformation"] = user_transformation * animation_transformation * iter->root_transformation;
shader["inverse_transpose_transformation"] = glm::transpose(glm::inverse(user_transformation * animation_transformation * iter->root_transformation));
}
else{
shader["transformation"] = transformation * iter->global_transformation;
shader["inverse_transpose_transformation"] = glm::transpose(glm::inverse(transformation * iter->global_transformation));
}
for(auto iter2 = iter->meshes.begin();iter2 != iter->meshes.end();++iter2){
const Mesh& mesh = meshes[*iter2];
const Material& material = materials[mesh.material_index];
texture_manager.get_texture(material.diffuse_texture_name).bind();
glDrawElements(GL_TRIANGLES,mesh.count,GL_UNSIGNED_INT,(void*)(mesh.begin*sizeof(unsigned int)));
}
}
vao.unbind();
}
void Model::fix_scene_mesh_indexes_after_changing_scene(std::vector<Scene>::iterator after, std::size_t fix)
{
for(auto iter = after;iter != scenes.end();++iter)
for(auto iter2 = iter->meshes.begin();iter2 != iter->meshes.end();++iter2)
*iter2 -= fix;
}
void Model::remove_boundingboxes_from_scene_data()
{
for(auto iter=scenes.begin();iter != scenes.end();){
if(is_bounding_box_mesh_name(iter->name) || is_hit_volume_mesh_name(iter->name)){
std::size_t deleted_meshes = iter->meshes.size();
fix_scene_mesh_indexes_after_changing_scene(iter+1, deleted_meshes);
iter = scenes.erase(iter);
}
else
++iter;
}
}
bool Model::operator ==(const Model& m)const
{
if(m.scenes.size() == scenes.size()){
for(std::size_t i=0;i<scenes.size();++i)
if(m.scenes[i].name != m.scenes[i].name)
return false;
return true;
}
return false;
}
void Model::recursive_extract_scene_formation(const aiNode* node, aiMatrix4x4 transformation, std::vector<glm::mat4> transformations)
{
Scene scene;
scene.root_transformation = glm::transpose(glm::mat4(transformation.a1, transformation.a2, transformation.a3, transformation.a4,
transformation.b1, transformation.b2, transformation.b3, transformation.b4,
transformation.c1, transformation.c2, transformation.c3, transformation.c4,
transformation.d1, transformation.d2, transformation.d3, transformation.d4));
transformation = transformation*node->mTransformation;
scene.name = std::string(node->mName.data);
scene.global_transformation = glm::transpose(glm::mat4(transformation.a1, transformation.a2, transformation.a3, transformation.a4,
transformation.b1, transformation.b2, transformation.b3, transformation.b4,
transformation.c1, transformation.c2, transformation.c3, transformation.c4,
transformation.d1, transformation.d2, transformation.d3, transformation.d4));
scene.local_transformation = glm::transpose(glm::mat4(node->mTransformation.a1, node->mTransformation.a2, node->mTransformation.a3, node->mTransformation.a4,
node->mTransformation.b1, node->mTransformation.b2, node->mTransformation.b3, node->mTransformation.b4,
node->mTransformation.c1, node->mTransformation.c2, node->mTransformation.c3, node->mTransformation.c4,
node->mTransformation.d1, node->mTransformation.d2, node->mTransformation.d3, node->mTransformation.d4));
transformations.push_back(scene.local_transformation);
scene.transformations = transformations;
for(unsigned int i=0;i<node->mNumMeshes;++i)
scene.meshes.push_back(node->mMeshes[i]);
if(!scene.meshes.empty())
scenes.push_back(scene);
for(unsigned int i=0;i<node->mNumChildren;++i)
recursive_extract_scene_formation(node->mChildren[i], transformation, transformations);
}
void Model::load_animations(const aiScene& scene)
{
for(unsigned int i=0;i<scene.mNumAnimations;++i){
aiAnimation& animation = *scene.mAnimations[i];
for(unsigned int j=0;j<animation.mNumChannels;++j){
Animation animation_;
aiNodeAnim& channel = *animation.mChannels[j];
animation_.initialize(channel, static_cast<float>(animation.mTicksPerSecond));
animations.push_back(animation_);
}
}
}
std::vector<std::string> Model::load_materials(const aiScene& scene)
{
std::vector<std::string> required_textures;
for(unsigned int i=0;i<scene.mNumMaterials;++i){
auto& material = *scene.mMaterials[i];
aiString texture_name;
material.GetTexture(aiTextureType_DIFFUSE,0,&texture_name);
std::string diffuse_texture_name = std::string(texture_name.data);
if(!diffuse_texture_name.empty()){
required_textures.push_back("textures/"+remove_absolute_address(Global::to_lower(diffuse_texture_name)));
materials.push_back(Material("textures/"+remove_absolute_address(Global::to_lower(diffuse_texture_name))));
}
else{
required_textures.push_back("textures/default.png");
materials.push_back(Material("textures/default.png"));
}
}
return required_textures;
}
std::string Model::remove_absolute_address(const std::string &file_name)
{
std::size_t last_slash = file_name.find_last_of('/');
if(last_slash == std::string::npos)
last_slash = file_name.find_last_of('\\');
if(last_slash != std::string::npos)
return file_name.substr(last_slash+1);
return file_name;
}
void Model::load_meshes(const aiScene& scene)
{
int indice_count = count_total_indice_count(scene);
int vertice_count = count_total_vertice_count(scene);
std::vector<unsigned int> indices;
std::vector<aiVector3D> vertices;
indices.reserve(indice_count);
vertices.resize(vertice_count);
int current_vertice_offset = 0;
int current_normal_offset = vertice_count/5;
int current_texture_offset = vertice_count/5*2;
int current_tangent_offset = vertice_count/5*3;
int current_bitangent_offset = vertice_count/5*4;
int current_indice_offset = 0;
for(unsigned int i=0;i<scene.mNumMeshes;++i){
auto& mesh = *scene.mMeshes[i];
if(is_bounding_box_mesh(i)){
bounding_boxes.push_back(calculate_bounding_box(mesh.mVertices, mesh.mVertices+mesh.mNumVertices, get_mesh_transformation(i)));
}
else if(is_hit_volume_mesh(i)){
if(is_hit_box_mesh(i))
hit_volume.hit_boxes.push_back(calculate_bounding_box(mesh.mVertices, mesh.mVertices+mesh.mNumVertices, get_mesh_transformation(i)));
else if(is_hit_sphere_mesh(i))
hit_volume.hit_spheres.push_back(calculate_bounding_sphere(mesh.mVertices, mesh.mVertices+mesh.mNumVertices, get_mesh_transformation(i)));
}
else{
int indices_last_size=indices.size();
for(auto iter = mesh.mFaces; iter != mesh.mFaces+mesh.mNumFaces;++iter){
for(auto iter2 = iter->mIndices; iter2 != iter->mIndices+iter->mNumIndices;++iter2)
indices.push_back(*iter2+current_indice_offset);
}
meshes.push_back(Mesh(indices_last_size,indices.size()-indices_last_size,mesh.mMaterialIndex));
current_indice_offset += mesh.mNumVertices;
std::copy(mesh.mVertices,mesh.mVertices+mesh.mNumVertices,vertices.begin()+current_vertice_offset);
current_vertice_offset += mesh.mNumVertices;
std::copy(mesh.mNormals,mesh.mNormals+mesh.mNumVertices,vertices.begin()+current_normal_offset);
current_normal_offset += mesh.mNumVertices;
if(mesh.mTextureCoords[0] != nullptr)
std::copy(mesh.mTextureCoords[0],mesh.mTextureCoords[0]+mesh.mNumVertices,vertices.begin()+current_texture_offset);
current_texture_offset += mesh.mNumVertices;
if(mesh.mTangents != nullptr)
std::copy(mesh.mTangents, mesh.mTangents+mesh.mNumVertices, vertices.begin()+current_tangent_offset);
current_tangent_offset += mesh.mNumVertices;
if(mesh.mBitangents!= nullptr)
std::copy(mesh.mBitangents, mesh.mBitangents+mesh.mNumVertices, vertices.begin()+current_bitangent_offset);
current_bitangent_offset += mesh.mNumVertices;
}
}
if(ibo.getId() == 0)
ibo.initialize(VertexBuffer::ELEMENT_ARRAY_BUFFER);
ibo.bufferData(indices,VertexBuffer::STATIC_DRAW);
if(vbo.getId() == 0)
vbo.initialize(VertexBuffer::ARRAY_BUFFER);
vbo.bufferData(vertices,VertexBuffer::STATIC_DRAW);
normal_offset = (void*)(vertice_count/5*sizeof(float)*3); //3 vertex
texture_offset = (void*)(vertice_count/5*sizeof(float)*6); //3 vertex + 3 normal
tangent_offset = (void*)(vertice_count/5*sizeof(float)*9); //3 vertex + 3 normal + 3 texcoords
bitangent_offset = (void*)(vertice_count/5*sizeof(float)*12); //3 vertex + 3 normal + 3 texcoords + 3 tangents
auto bb_geometry = create_bounding_box_geometry();
if(bounding_box_vbo.getId() == 0)
bounding_box_vbo.initialize(VertexBuffer::ARRAY_BUFFER);
bounding_box_vbo.bufferData(bb_geometry,VertexBuffer::STATIC_DRAW);
}
bool Model::Scene::contains_mesh(unsigned int mesh_index)const
{
for(auto iter = meshes.begin();iter != meshes.end();++iter)
if(*iter == mesh_index)
return true;
return false;
}
glm::mat4 Model::get_mesh_transformation(unsigned int mesh_index)const
{
for(auto iter = scenes.begin();iter != scenes.end();++iter){
if(iter->contains_mesh(mesh_index))
return iter->global_transformation;
}
return glm::mat4();
}
bool Model::is_bounding_box_mesh(unsigned int mesh_index)const
{
for(auto iter = scenes.begin();iter != scenes.end();++iter){
if(is_bounding_box_mesh_name(iter->name))
if(iter->contains_mesh(mesh_index))
return true;
}
return false;
}
bool Model::is_bounding_box_mesh_name(const std::string& mesh_name)const
{
return Global::begins_with(mesh_name, "bb_");
}
bool Model::is_hit_volume_mesh(unsigned int mesh_index)const
{
for(auto iter = scenes.begin();iter != scenes.end();++iter){
if(is_hit_volume_mesh_name(iter->name))
if(iter->contains_mesh(mesh_index))
return true;
}
return false;
}
bool Model::is_hit_sphere_mesh(unsigned int mesh_index)const
{
for(auto iter = scenes.begin();iter != scenes.end();++iter){
if(is_hit_sphere_mesh_name(iter->name))
if(iter->contains_mesh(mesh_index))
return true;
}
return false;
}
bool Model::is_hit_box_mesh(unsigned int mesh_index)const
{
for(auto iter = scenes.begin();iter != scenes.end();++iter){
if(is_hit_box_mesh_name(iter->name))
if(iter->contains_mesh(mesh_index))
return true;
}
return false;
}
bool Model::is_hit_volume_mesh_name(const std::string& mesh_name)const
{
return is_hit_box_mesh_name(mesh_name) || is_hit_sphere_mesh_name(mesh_name);
}
bool Model::is_hit_sphere_mesh_name(const std::string& mesh_name)const
{
return Global::begins_with(mesh_name, "hs_");
}
bool Model::is_hit_box_mesh_name(const std::string& mesh_name)const
{
return Global::begins_with(mesh_name, "hb_");
}
BoundingSphere Model::calculate_bounding_sphere(const aiVector3D* begin, const aiVector3D* end, const glm::mat4 &transformation)
{
BoundingSphere bs;
BoundingBox bb = calculate_bounding_box(begin, end, transformation);
bs.position = bb.get_middle_position();
glm::vec3 side = bb.min;
side.y = (bb.min.y+bb.max.y)/2.0f;
side.z = (bb.min.z+bb.max.z)/2.0f;
bs.radius = Radius(glm::distance(bs.position, side));
return bs;
}
BoundingBox Model::calculate_bounding_box(const aiVector3D* begin, const aiVector3D* end, const glm::mat4 &transformation)
{
//glm::quat rotation = glm::quat_cast(transformation);
//auto real = glm::angle(rotation);
//glm::vec3 angles = glm::axis(rotation);
BoundingBox bb;
bb.min = glm::vec3(std::numeric_limits<float>::max());
bb.max = glm::vec3(std::numeric_limits<float>::lowest());
for(auto iter = begin;iter != end;++iter){
glm::vec4 vertex(iter->x,iter->y,iter->z,1);
vertex = transformation*vertex;
//bb.rotation = rotation;
//bb.transformation = transformation;
if(vertex.x < bb.min.x)
bb.min.x = vertex.x;
if(vertex.y < bb.min.y)
bb.min.y = vertex.y;
if(vertex.z < bb.min.z)
bb.min.z = vertex.z;
if(vertex.x > bb.max.x)
bb.max.x = vertex.x;
if(vertex.y > bb.max.y)
bb.max.y = vertex.y;
if(vertex.z > bb.max.z)
bb.max.z = vertex.z;
}
return bb;
}
BoundingBox Model::calculate_bounding_box(std::vector<glm::vec3>::iterator begin, std::vector<glm::vec3>::iterator end, const glm::mat4 &transformation)
{
BoundingBox bb;
bb.min = glm::vec3(std::numeric_limits<float>::max());
bb.max = glm::vec3(std::numeric_limits<float>::lowest());
for(auto iter = begin;iter != end;++iter){
glm::vec4 vertex(iter->x,iter->y,iter->z,1);
vertex = transformation*vertex;
if(vertex.x < bb.min.x)
bb.min.x = vertex.x;
if(vertex.y < bb.min.y)
bb.min.y = vertex.y;
if(vertex.z < bb.min.z)
bb.min.z = vertex.z;
if(vertex.x > bb.max.x)
bb.max.x = vertex.x;
if(vertex.y > bb.max.y)
bb.max.y = vertex.y;
if(vertex.z > bb.max.z)
bb.max.z = vertex.z;
}
return bb;
}
std::vector<glm::vec3> Model::create_bounding_box_geometry()
{
std::vector<glm::vec3> vertices;
for(auto iter = bounding_boxes.begin();iter != bounding_boxes.end();++iter){
auto cube = create_line_loop_cube(iter->min, iter->max);
vertices.insert(vertices.end(),cube.begin(),cube.end());
}
return vertices;
}
int Model::count_total_vertice_count(const aiScene& scene)const
{
int vertices=0;
for(unsigned int i=0;i<scene.mNumMeshes;++i)
if(!is_bounding_box_mesh(i) || !is_hit_volume_mesh(i))
vertices += scene.mMeshes[i]->mNumVertices*5; //assume we always have vertex,normal, tangent, bitangent and texture coordinates
return vertices;
}
int Model::count_total_indice_count(const aiScene& scene)const
{
int indices=0;
for(unsigned int i=0;i<scene.mNumMeshes;++i)
if(!is_bounding_box_mesh(i) || !is_hit_volume_mesh(i))
for(auto iter=scene.mMeshes[i]->mFaces;iter != scene.mMeshes[i]->mFaces+scene.mMeshes[i]->mNumFaces;++iter)
indices += iter->mNumIndices; //assume we always have vertex,normal,tangent and texture coordinates
return indices;
}
const Animation* Model::get_possible_animation(const std::string& mesh_name)const
{
for(auto iter = animations.begin();iter != animations.end();++iter)
if(iter->affected_mesh == mesh_name)
return &*iter;
return nullptr;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment