#include "polygon_model.hpp" #include void discardLastGlError() { glGetError(); } void checkAndPrintGlError() { GLenum err = glGetError(); const char *errString; switch(err) { case GL_INVALID_ENUM: errString = "GL_INVALID_ENUM"; break; case GL_INVALID_VALUE: errString = "GL_INVALID_VALUE"; break; case GL_INVALID_OPERATION: errString = "GL_INVALID_OPERATION"; break; case GL_OUT_OF_MEMORY: errString = "GL_OUT_OF_MEMORY"; break; case GL_INVALID_FRAMEBUFFER_OPERATION: errString = "GL_INVALID_FRAMEBUFFER_OPERATION"; break; default: errString = ""; break; } if (err != GL_NO_ERROR) { std::cout<<"glGetAttribLocation() returned error: " << errString << std::endl; } } namespace endofthejedi { PolygonModel::PolygonModel(const std::string &filename) : m_filename(filename) { clear(); } // TODO: add change/set shader function because it stays for all calls of an // instance the same. void PolygonModel::setup(Shader *shader) { if (shader == nullptr) { std::cout<<"shader is NULL" << std::endl; exit(-1); return; } if (shader->program() == 0) { std::cout<<"program is invalid" << std::endl; exit(-1); return; } std::cout<<"program: " << shader->program() << std::endl; m_shader = shader; // TODO error checks const char *names[] = { "in_vertex", "in_normal" }; discardLastGlError(); for (int i=0; i<2; i++) { const char *name = names[i]; GLint loc = glGetAttribLocation(m_shader->program(), name); m_attr_locations[i] = loc; checkAndPrintGlError(); if (m_attr_locations[i] == -1) { std::cerr<<"[polygonmodel] warning: attribute location #" << i << " (for '" << name << "') is invalid!" << std::endl; } else { std::cout<<"[polygonmodel] attribute location #" << i << " (for '" << name << "') is " << m_attr_locations[i] << std::endl; } } } bool PolygonModel::bind() { if (!m_loaded_to_opengl) { std::cout<<"[polygonmodel] warning: try to bind model vbo " << "which was not uploaded to OpenGL!" << std::endl; exit(-1); return false; } // bind position vbo for (int i=0; i<2; i++) { glEnableVertexAttribArray(m_attr_locations[0]); glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id[i]); glVertexAttribPointer(m_attr_locations[i], 3, GL_FLOAT, GL_FALSE, 0, NULL); } m_binding_active = true; return true; } bool PolygonModel::render() { if (!m_binding_active || !m_loaded_to_opengl) { std::cout<<"[polygonmodel] warning: try to render model without bind()" << std::endl; exit(-1); return false; } glDrawArrays(GL_TRIANGLES, 0, m_numVertices); return true; } bool PolygonModel::copyVertices(const aiScene *scene) { if (scene->mMeshes == 0) { std::cout << "[polygonmodel : no meshes loaded for " << m_filename << std::endl; return false; } aiNode *node = scene->mRootNode; const aiMesh* mesh = scene->mMeshes[node->mMeshes[0]]; // 3 vertices per face, 3 floats per vertex m_numVertices = mesh->mNumFaces*3; m_data_position.reserve(m_numVertices); m_data_normal.reserve(m_numVertices); size_t t, i; for (t=0; tmNumFaces; ++t) { const aiFace* face = &mesh->mFaces[t]; if (face->mNumIndices != 3) { std::cout << "[polygonmodel] need triangles, got something different with: " << face->mNumIndices << " vertices" << std::endl; return false; } for (i=0; imNumIndices; i++) { const size_t index = face->mIndices[i]; m_data_position.push_back(mesh->mVertices[index].x); m_data_position.push_back(mesh->mVertices[index].y); m_data_position.push_back(mesh->mVertices[index].z); m_data_normal.push_back(mesh->mNormals[index].x); m_data_normal.push_back(mesh->mNormals[index].y); m_data_normal.push_back(mesh->mNormals[index].z); //std::cout<<"adding normal: " << mesh->mNormals[index].x << " " << mesh->mNormals[index].y << " " << mesh->mNormals[index].z << std::endl; } } size_t totalBytes = 3*m_numVertices*sizeof(float); std::cout<<"[polygonmodel] loaded file " << m_filename << " with " << m_numVertices << " vertices (" << totalBytes << " bytes)" << std::endl; return true; } bool PolygonModel::uploadToOpenGl() { if (!m_loaded_from_file) { std::cerr<<"[polygonmodel] warning: try to upload model data " << "to OpenGL but no data is loaded!" << std::endl; exit(-1); return false; } /************************************************************/ /* Position data */ /************************************************************/ glGenBuffers(2, m_vbo_id); // Generate buffer glEnableVertexAttribArray(m_attr_locations[0]); glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id[0]); // Bind buffer glVertexAttribPointer( m_attr_locations[0], 3, // three floats per vertex GL_FLOAT, // Data is floating point type GL_FALSE, // No fixed point scaling 0, // stride: no NULL); // No offset // fill buffer with the loaded mesh position data glBufferData( GL_ARRAY_BUFFER, // Buffer target 3 * m_numVertices * sizeof(float), // Buffer data size m_data_position.data(), // Buffer data pointer GL_STATIC_DRAW); // Usage - Data never changes; /************************************************************/ /* Normal data */ /************************************************************/ glEnableVertexAttribArray(m_attr_locations[1]); glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id[1]); // Bind buffer glVertexAttribPointer( m_attr_locations[1], 3, // three floats per normal GL_FLOAT, // Data is floating point type GL_FALSE, // No fixed point scaling 0, // stride: no NULL); // No offset // Fill bound buffer glBufferData( GL_ARRAY_BUFFER, // Buffer target 3 * m_numVertices * sizeof(float), // Buffer data size m_data_normal.data(), // Buffer data pointer GL_STATIC_DRAW); // Usage - Data never changes; m_loaded_to_opengl = true; return true; } bool PolygonModel::import() { clear(); // Create an instance of the Importer class Assimp::Importer importer; // And have it read the given file with some example postprocessing // Usually - if speed is not the most important aspect for you - you'll // propably to request more postprocessing than we do in this example. const aiScene* scene = importer.ReadFile(m_filename, aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType); // If the import failed, report it if (!scene) { std::cout<<"[polygonmodel] loading file " << m_filename << " failed with: " << importer.GetErrorString() << std::endl; return false; } // Now we can access the file's contents. copyVertices(scene); m_loaded_from_file = true; // We're done. Everything will be cleaned up by the importer destructor return true; } void PolygonModel::clear() { // TODO: delete buffers if there's data m_loaded_from_file = false; m_loaded_to_opengl = false; m_data_position.clear(); m_data_normal.clear(); for (int i=0; i<2; i++) { m_vbo_id[i] = -1; m_attr_locations[i] = -1; } m_numVertices = 0; } }