assimp not loading correctly - opengl

I'm a having problem loading meshes using Assimp. Some of the faces are not displayed even after ambient lighting. I'm using the code provided in the learnopengl.com tutorials for loading the mesh. I've included my Mesh and Model source below and the screenshot as well. If anyone can help with issue, I would really be very grateful.
Mesh.h
#pragma once
// Std. Includes
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
using namespace std;
// GL Includes
#include <GL/glew.h> // Contains all the necessery OpenGL includes
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
struct Vertex {
// Position
glm::vec3 Position;
// Normal
glm::vec3 Normal;
// TexCoords
glm::vec2 TexCoords;
};
struct Texture {
GLuint id;
string type;
aiString path;
};
class Mesh {
public:
/* Mesh Data */
vector<Vertex> vertices;
vector<GLuint> indices;
vector<Texture> textures;
/* Functions */
// Constructor
Mesh(vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> textures)
{
this->vertices = vertices;
this->indices = indices;
this->textures = textures;
// Now that we have all the required data, set the vertex buffers and its attribute pointers.
this->setupMesh();
}
// Render the mesh
void Draw(Shader shader)
{
// Bind appropriate textures
GLuint diffuseNr = 1;
GLuint specularNr = 1;
GLuint reflectionNr = 1;
for (GLuint i = 0; i < this->textures.size(); i++)
{
glActiveTexture(GL_TEXTURE0 + i); // Active proper texture unit before binding
// Retrieve texture number (the N in diffuse_textureN)
stringstream ss;
string number;
string name = this->textures[i].type;
if (name == "texture_diffuse")
ss << diffuseNr++; // Transfer GLuint to stream
else if (name == "texture_specular")
ss << specularNr++; // Transfer GLuint to stream
else if (name == "texture_reflection") // We'll now also need to add the code to set and bind to reflection textures
ss << reflectionNr++;
number = ss.str();
// Now set the sampler to the correct texture unit
glUniform1i(glGetUniformLocation(shader.program, (name + number).c_str()), i);
// And finally bind the texture
glBindTexture(GL_TEXTURE_2D, this->textures[i].id);
}
glActiveTexture(GL_TEXTURE0); // Always good practice to set everything back to defaults once configured.
// Also set each mesh's shininess property to a default value (if you want you could extend this to another mesh property and possibly change this value)
//glUniform1f(glGetUniformLocation(shader.Program, "material.shininess"), 16.0f);
// Draw mesh
glBindVertexArray(this->VAO);
glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
private:
/* Render data */
GLuint VAO, VBO, EBO;
/* Functions */
// Initializes all the buffer objects/arrays
void setupMesh()
{
// Create buffers/arrays
glGenVertexArrays(1, &this->VAO);
glGenBuffers(1, &this->VBO);
glGenBuffers(1, &this->EBO);
glBindVertexArray(this->VAO);
// Load data into vertex buffers
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
// A great thing about structs is that their memory layout is sequential for all its items.
// The effect is that we can simply pass a pointer to the struct and it translates perfectly to a glm::vec3/2 array which
// again translates to 3/2 floats which translates to a byte array.
glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(Vertex), &this->vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(GLuint), &this->indices[0], GL_STATIC_DRAW);
// Set the vertex attribute pointers
// Vertex Positions
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0);
// Vertex Normals
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Normal));
// Vertex Texture Coords
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, TexCoords));
glBindVertexArray(0);
}
};
Model.h
#pragma once
// Std. Includes
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <map>
#include <vector>
using namespace std;
// GL Includes
#include <GL/glew.h> // Contains all the necessery OpenGL includes
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <SOIL.h>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include "Mesh.h"
GLint TextureFromFile(const char* path, string directory);
class Model
{
public:
/* Functions */
// Constructor, expects a filepath to a 3D model.
Model(GLchar* path)
{
this->loadModel(path);
}
// Draws the model, and thus all its meshes
void Draw(Shader shader)
{
for (GLuint i = 0; i < this->meshes.size(); i++)
this->meshes[i].Draw(shader);
}
private:
/* Model Data */
vector<Mesh> meshes;
string directory;
vector<Texture> textures_loaded; // Stores all the textures loaded so far, optimization to make sure textures aren't loaded more than once.
/* Functions */
// Loads a model with supported ASSIMP extensions from file and stores the resulting meshes in the meshes vector.
void loadModel(string path)
{
// Read file via ASSIMP
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
// Check for errors
if (!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero
{
cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl;
return;
}
// Retrieve the directory path of the filepath
this->directory = path.substr(0, path.find_last_of('/'));
// Process ASSIMP's root node recursively
this->processNode(scene->mRootNode, scene);
}
// Processes a node in a recursive fashion. Processes each individual mesh located at the node and repeats this process on its children nodes (if any).
void processNode(aiNode* node, const aiScene* scene)
{
// Process each mesh located at the current node
for (GLuint i = 0; i < node->mNumMeshes; i++)
{
// The node object only contains indices to index the actual objects in the scene.
// The scene contains all the data, node is just to keep stuff organized.
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
this->meshes.push_back(this->processMesh(mesh, scene));
}
// After we've processed all of the meshes (if any) we then recursively process each of the children nodes
for (GLuint i = 0; i < node->mNumChildren; i++)
{
// Child nodes are actually stored in the node, not in the scene (which makes sense since nodes only contain
// links and indices, nothing more, so why store that in the scene)
this->processNode(node->mChildren[i], scene);
}
}
Mesh processMesh(aiMesh* mesh, const aiScene* scene)
{
// Data to fill
vector<Vertex> vertices;
vector<GLuint> indices;
vector<Texture> textures;
// Walk through each of the mesh's vertices
for (GLuint i = 0; i < mesh->mNumVertices; i++)
{
Vertex vertex;
glm::vec3 vector; // We declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to glm's vec3 class so we transfer the data to this placeholder glm::vec3 first.
// Positions
vector.x = mesh->mVertices[i].x;
vector.y = mesh->mVertices[i].y;
vector.z = mesh->mVertices[i].z;
vertex.Position = vector;
// Normals
vector.x = mesh->mNormals[i].x;
vector.y = mesh->mNormals[i].y;
vector.z = mesh->mNormals[i].z;
vertex.Normal = vector;
// Texture Coordinates
if (mesh->mTextureCoords[0]) // Does the mesh contain texture coordinates?
{
glm::vec2 vec;
// A vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't
// use models where a vertex can have multiple texture coordinates so we always take the first set (0).
vec.x = mesh->mTextureCoords[0][i].x;
vec.y = mesh->mTextureCoords[0][i].y;
vertex.TexCoords = vec;
}
else
vertex.TexCoords = glm::vec2(0.0f, 0.0f);
vertices.push_back(vertex);
}
// Now wak through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices.
for (GLuint i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];
// Retrieve all indices of the face and store them in the indices vector
for (GLuint j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
// Process materials
if (mesh->mMaterialIndex >= 0)
{
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
// We assume a convention for sampler names in the shaders. Each diffuse texture should be named
// as 'texture_diffuseN' where N is a sequential number ranging from 1 to MAX_SAMPLER_NUMBER.
// Same applies to other texture as the following list summarizes:
// Diffuse: texture_diffuseN
// Specular: texture_specularN
// Normal: texture_normalN
// 1. Diffuse maps
vector<Texture> diffuseMaps = this->loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
// 2. Specular maps
vector<Texture> specularMaps = this->loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
// 3. Reflection maps (Note that ASSIMP doesn't load reflection maps properly from wavefront objects, so we'll cheat a little by defining the reflection maps as ambient maps in the .obj file, which ASSIMP is able to load)
vector<Texture> reflectionMaps = this->loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_reflection");
textures.insert(textures.end(), reflectionMaps.begin(), reflectionMaps.end());
}
// Return a mesh object created from the extracted mesh data
return Mesh(vertices, indices, textures);
}
// Checks all material textures of a given type and loads the textures if they're not loaded yet.
// The required info is returned as a Texture struct.
vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName)
{
vector<Texture> textures;
for (GLuint i = 0; i < mat->GetTextureCount(type); i++)
{
aiString str;
mat->GetTexture(type, i, &str);
// Check if texture was loaded before and if so, continue to next iteration: skip loading a new texture
GLboolean skip = false;
for (GLuint j = 0; j < textures_loaded.size(); j++)
{
if (textures_loaded[j].path == str)
{
textures.push_back(textures_loaded[j]);
skip = true; // A texture with the same filepath has already been loaded, continue to next one. (optimization)
break;
}
}
if (!skip)
{ // If texture hasn't been loaded already, load it
Texture texture;
texture.id = TextureFromFile(str.C_Str(), this->directory);
texture.type = typeName;
texture.path = str;
textures.push_back(texture);
this->textures_loaded.push_back(texture); // Store it as texture loaded for entire model, to ensure we won't unnecesery load duplicate textures.
}
}
return textures;
}
};
GLint TextureFromFile(const char* path, string directory)
{
//Generate texture ID and load texture data
string filename = string(path);
filename = directory + '/' + filename;
GLuint textureID;
glGenTextures(1, &textureID);
int width, height;
unsigned char* image = SOIL_load_image(filename.c_str(), &width, &height, 0, SOIL_LOAD_RGB);
// Assign texture to ID
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
// Parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
SOIL_free_image_data(image);
return textureID;
}
Screenshots
1st screenshot
2nd screenshot

as I can see here
glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);
you draw all vertexes as triangles. And that is how you load indices from assimp
for (GLuint i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];
for (GLuint j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
There is a problem here- face doesn't have exacly 3 vertexes (even if you use aiProcess_Triangulate when loading) which brokes drawing with GL_TRIANGLES. With aiProcess_Triangulate there will be faces with <= 3 vertexes (triangles and lines for example). You may ignore not triangle faces and that should fix drawing. Here is a fixed indeces filling cycle:
for (GLuint i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];
if (face.mNumIndices < 3) {
continue;
}
assert(face.mNumIndices == 3);
for (GLuint j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}

I know this is old, but as I had the same problem, I thought this might help other people as well:
In my case my texture coordinates were not correct and the problem was in the lines
vertices.push_back(mesh->mTextureCoords[0][i].x);
vertices.push_back(mesh->mTextureCoords[0][i].y);
In this case, we iterate over all vertices of an aiScene.
This lead in my case to your bug where not all faces were displayed.
Adding vertices via faces, however, fixed the issue for me:
const aiScene *tree = _importer.ReadFile(path, aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType);
// iterate over all meshes in this scene
for (unsigned int m = 0; m < tree->mNumMeshes; ++m) {
const aiMesh *mesh = tree->mMeshes[m];
// iterate over all faces in this mesh
for (unsigned int j = 0; j < mesh->mNumFaces; ++j) {
auto const &face = mesh->mFaces[j];
//normally you want just triangles, so iterate over all 3 vertices of the face:
for (int k = 0; k < 3; ++k) {
// Now do the magic with 'face.mIndices[k]'
auto const &vertex = mesh->mVertices[face.mIndices[k]];
vertices.push_back(vertex.x);
vertices.push_back(vertex.y);
vertices.push_back(vertex.z);
// Same for the normals.
auto const &normal = mesh->mNormals[face.mIndices[k]];
vertices.push_back(normal.x);
vertices.push_back(normal.y);
vertices.push_back(normal.z);
// Color of material
// ...
// And FINALLY: The UV coordinates!
if(mesh->HasTextureCoords(0)) {
// The following line fixed the issue for me now:
auto const &uv = mesh->mTextureCoords[0][face.mIndices[k]];
vertices.push_back(uv.x);
vertices.push_back(uv.y);
}
}
}
}

Related

Trying to draw sphere in OpenGL, Only part of the sphere is showing, seems clipped?

I created a minimal setup with a fragment shader setting the color to write, so not even a parameter.
The vertex shader passes in a matrix and transforms the points. We can see the sphere, but only part of it.
I hesitate to post the whole code, trying as hard as possible for a minimum working solution but it's about 300 lines including the shader loading code. I will post just the core pieces, and if people want more I will post it all.
Here is the code for the demo including a stripped down Sphere class and glmain.
Not shown is main() which does try..catch and calls glmain
#include <GL/glew.h>
#include "common/common.hh"
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include <numbers>
#include <iostream>
#include <iomanip>
#include <cstdint>
#include <string>
using namespace std;
using namespace glm;
using namespace std::numbers;
class Sphere {
private:
uint32_t progid; // handle to the shader code
uint32_t vao; // array object container for vbo and indices
uint32_t vbo; // handle to the point data on the graphics card
uint32_t lbo; // handle to buffer of indices for lines for wireframe sphere
uint32_t latRes, lonRes;
uint32_t resolution;
public:
/**
* #brief Construct a sphere
*
* #param r radius of the sphere
* #param latRes resolution of the grid in latitude
* #param lonRes resolution of the grid in latitude
*/
Sphere(double r, uint32_t latRes, uint32_t lonRes);
~Sphere() { cleanup(); }
void render(mat4& trans);
void cleanup();
};
Sphere::Sphere(double r, uint32_t latRes, uint32_t lonRes) : latRes(latRes), lonRes(lonRes),
resolution((2*latRes-2)*lonRes + 2) {
progid = loadShaders( "05_3d.vert", "02simple.frag" );
double dlon = 2.0*numbers::pi / lonRes, dlat = numbers::pi / latRes;
double z;
double lat = -numbers::pi/2 + dlat; // latitude in radians
double rcircle;
float vert[resolution*3]; // x,y,z
uint32_t c = 0;
for (uint32_t j = 0; j < 2*latRes-2; j++, lat += dlat) {
//what is the radius of hte circle at that height?
rcircle = r* cos(lat); // size of the circle at this latitude
z = r * sin(lat); // height of each circle
double t = 0;
for (uint32_t i = 0; i < lonRes; i++, t += dlon) {
vert[c++] = rcircle * cos(t), vert[c++] = rcircle * sin(t);
vert[c++] = z;
}
cout << endl;
}
// south pole
vert[c++] = 0;
vert[c++] = 0;
vert[c++] = -r;
// north pole
vert[c++] = 0;
vert[c++] = 0;
vert[c++] = r;
cout << "resolution: " << resolution << endl;
cout << "predicted num vert components: " << resolution*3 << endl;
cout << "actual num vert components: " << c << endl;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, resolution, vert, GL_STATIC_DRAW);
glBindVertexArray(0);
}
void Sphere::render(mat4& trans) {
glUseProgram(progid); // Use the shader
uint32_t matrixID = glGetUniformLocation(progid, "trans");
glUniformMatrix4fv(matrixID, 1, GL_FALSE, &trans[0][0]);
glBindVertexArray(vao);
glVertexAttribPointer(
0, // first parameter to shader, numbered 0
3, // 3 floating point numbers (x,y,z)
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // this is the entire set of data, move on
(void*)0 // array buffer offset
);
glEnableVertexAttribArray(0); // pass x,y to shader
glEnable(GL_PROGRAM_POINT_SIZE);
//points don't work, why not? And how to set the size of the points?
glPointSize(5);
glDrawArrays(GL_POINT, 0, resolution);
// line strips work, but incomplete (see screen shot)
glDrawArrays(GL_LINE_STRIP, 0, resolution);
glDisableVertexAttribArray(0);
}
void Sphere::cleanup() {
glDeleteBuffers(1, &vbo); // remove vbo memory from graphics card
glDeleteVertexArrays(1, &vao); // remove vao from graphics card
glDeleteProgram(progid);
}
using namespace std;
void glmain() {
win = createWindow(800, 800, "Sphere demo");
glClearColor(0.0f, 0.0f, 0.4f, 0.0f); // Dark blue background
Sphere sphere(1.0, 30, 15);
mat4 trans= lookAt(vec3(0,0,0), vec3(10,5,10), vec3(0,1,0));
do {
glClear( GL_COLOR_BUFFER_BIT ); // Clear the screen
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
//glDepthFunc(GL_LESS);
sphere.render(trans);
glfwSwapBuffers(win); // double buffer
glfwPollEvents();
} while( glfwGetKey(win, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
glfwWindowShouldClose(win) == 0 );
}
Points did not display at all so the call is commented out. We drew a line strip instead. That works somewhat. Why is it truncated? Why doesn't it at least finish the layer of the sphere?
The shaders are shown below:
#version 330 core
// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 v;
uniform mat4 trans;
void main(){
gl_PointSize = 5;
gl_Position = trans * vec4(v,1.0);
gl_Position.w = 1.0;
}
fragment shader:
#version 330 core
out vec4 color;
void main()
{
color = vec4(1,1,1,1);
}
The size argument of glBufferData specifies the size in bytes of the buffer object's new data store:
glBufferData(GL_ARRAY_BUFFER, resolution, vert, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, resolution * 3 * sizeof(float), vert, GL_STATIC_DRAW);

Huge performance drop on higher window resolutions with large models in OpenGL [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 months ago.
Improve this question
I am currently rendering about 10 backpack objects and a huge sponza model with OpenGL (which has about 400 meshes and 2 million triangles), and 7 point lights in my scene. The performance is ok (60fps) when the window is in 800x600 resolution, however when I make the window about the size of my screen (2560x1600), performance hugely drops to about 15-20 fps, especially when looking through the sponza model (I am using Assimp). The code structure is like the following (simplified):
Mesh.cpp:
class Mesh {
// class that contains all vertex info (pos, normal, UV)
void draw(const Shader& shader) const {
// bind the textures + activate
// bind the vao
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
}
};
Model.cpp (only job is to load the meshes):
class Model {
std::vector<Mesh> meshes;
void draw(const Shader& shader) const {
for (const auto& mesh: meshes) {
mesh.draw(shader);
}
}
};
The thing is, since sponza model has 400 meshes, this corresponds to 400 draw calls + 10 backpack draw calls which roughly equals to 410 draw calls per frame. I guess that making a draw call for each mesh might create this performance drop. However, I am unsure how to solve it. Maybe I can put all the vertex data into one VBO and make one draw call, but I'm not exactly sure how to tackle that. Also, there are 25 materials in the sponza model.
P.S. I am using learnopengl.com's implementation of Mesh and Model, and here are the complete classes:
Model.cpp
class Model {
public:
explicit Model(const std::string &path);
~Model();
void draw(const Shader &shader);
private:
void load_model(const std::string &path);
void process_node(aiNode *node, const aiScene *scene);
Mesh process_mesh(aiMesh *mesh, const aiScene *scene);
std::vector<Texture> load_material_textures(aiMaterial *mat, aiTextureType type,
Texture::TextureType tex_type);
std::vector<Mesh> meshes;
std::vector<Texture> textures_loaded;
std::string directory;
};
#include <string>
Model::Model(const std::string &path) {
load_model(path);
}
Model::~Model() {
for (const auto &mesh : meshes) {
mesh.destroy();
}
}
void Model::draw(const Shader &shader) {
for (const auto &mesh : meshes)
mesh.draw(shader);
}
void Model::load_model(const std::string &path) {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(path.c_str(), aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenSmoothNormals | aiProcess_JoinIdenticalVertices);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl;
return;
}
directory = path.substr(0, path.find_last_of("/"));
std::cout << "load_model(" << path << ")" << std::endl;
process_node(scene->mRootNode, scene);
std::cout << meshes.size() << std::endl;
}
void Model::process_node(aiNode *node, const aiScene *scene) {
// process each mesh located at the current node
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
// the node object only contains indices to index the actual objects in the scene.
// the scene contains all the data, node is just to keep stuff organized (like relations between nodes).
aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(process_mesh(mesh, scene));
}
// after we've processed all of the meshes (if any) we then recursively process each of the children nodes
for (unsigned int i = 0; i < node->mNumChildren; i++) {
process_node(node->mChildren[i], scene);
}
}
Mesh Model::process_mesh(aiMesh *mesh, const aiScene *scene) {
std::vector<Vertex> vertices;
std::vector<Texture> textures;
std::vector<ui32> indices;
// fill in vertex data form the model info
for (int i = 0; i < mesh->mNumVertices; i++) {
Vertex vertex;
glm::vec3 temp_vec;
// get the position data
temp_vec.x = mesh->mVertices[i].x;
temp_vec.y = mesh->mVertices[i].y;
temp_vec.z = mesh->mVertices[i].z;
vertex.position = temp_vec;
// get the normal data
temp_vec.x = mesh->mNormals[i].x;
temp_vec.y = mesh->mNormals[i].y;
temp_vec.z = mesh->mNormals[i].z;
vertex.normals = temp_vec;
// get the uv data
if (mesh->mTextureCoords[0]) {
glm::vec2 uv_data;
uv_data.x = mesh->mTextureCoords[0][i].x;
uv_data.y = mesh->mTextureCoords[0][i].y;
vertex.uv = uv_data;
} else
vertex.uv = glm::vec2(0.0f, 0.0f);
vertices.push_back(vertex);
}
for (int i = 0; i < mesh->mNumFaces; i++) {
aiFace face = mesh->mFaces[i];
for (int j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
if (mesh->mMaterialIndex >= 0) {
aiMaterial *mat = scene->mMaterials[mesh->mMaterialIndex];
std::vector<Texture> diffuse_maps = load_material_textures(mat, aiTextureType_DIFFUSE, Texture::TextureType::DIFFUSE);
textures.insert(textures.end(), diffuse_maps.begin(), diffuse_maps.end());
std::vector<Texture> specular_maps = load_material_textures(mat, aiTextureType_SPECULAR, Texture::TextureType::SPECULAR);
textures.insert(textures.end(), specular_maps.begin(), specular_maps.end());
std::vector<Texture> emission_maps = load_material_textures(mat, aiTextureType_EMISSIVE, Texture::TextureType::EMISSION);
textures.insert(textures.end(), emission_maps.begin(), emission_maps.end());
}
return Mesh(vertices, indices, textures);
}
std::vector<Texture> Model::load_material_textures(aiMaterial *mat, aiTextureType type, Texture::TextureType tex_type) {
std::vector<Texture> textures;
for (int i = 0; i < mat->GetTextureCount(type); i++) {
aiString str;
mat->GetTexture(type, i, &str);
bool skip = false;
for (int j = 0; j < textures_loaded.size(); j++) {
if (std::strcmp(textures_loaded[j].path.data(), str.C_Str()) == 0) {
textures.push_back(textures_loaded[j]);
skip = true;
break;
}
}
if (!skip) {
Texture tex = ResourceManager::load_ogl_texture_from_path(directory + "/" + str.C_Str(), tex_type);
tex.path = str.C_Str();
textures.push_back(tex);
textures_loaded.push_back(tex);
}
}
return textures;
}
Mesh.cpp:
struct Vertex {
glm::vec3 position;
glm::vec3 normals;
glm::vec2 uv;
};
class Mesh {
public:
std::vector<Vertex> vertices;
std::vector<ui32> indices;
std::vector<Texture> textures;
Mesh(const std::vector<Vertex> &vertices, const std::vector<ui32> &indices, const std::vector<Texture> &textures);
~Mesh();
void draw(const Shader &shader) const;
void destroy() const;
private:
ui32 vao, vbo, ebo;
void setup_mesh();
};
Mesh::Mesh(const std::vector<Vertex> &vertices, const std::vector<ui32> &indices, const std::vector<Texture> &textures) : vertices(vertices), indices(indices), textures(textures) {
setup_mesh();
}
Mesh::~Mesh() {}
void Mesh::setup_mesh() {
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int),
&indices[0], GL_STATIC_DRAW);
// vertex positions
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr);
// vertex normals
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *) offsetof(Vertex, normals));
// vertex texture coords
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *) offsetof(Vertex, uv));
glBindVertexArray(0);
}
void Mesh::draw(const Shader &shader) const {
ui32 no_diffuse = 1;
ui32 no_specular = 1;
ui32 no_emission = 1;
for (int i = 0; i < textures.size(); i++) {
glActiveTexture(GL_TEXTURE0 + i);
std::string n, base_name;
base_name = textures[i].type;
if (std::strcmp(base_name.c_str(), "texture_diffuse") == 0)
n = std::to_string(no_diffuse++);
else if (std::strcmp(base_name.c_str(), "texture_specular") == 0)
n = std::to_string(no_specular++);// transfer unsigned int to string
else if (std::strcmp(base_name.c_str(), "texture_emission") == 0)
n = std::to_string(no_emission++);// transfer unsigned int to string
shader.setInt("material." + base_name + n, i);
glBindTexture(GL_TEXTURE_2D, textures[i].id);
}
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glActiveTexture(GL_TEXTURE0);
}
void Mesh::destroy() const {
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ebo);
for (const auto &texture : textures)
glDeleteTextures(1, &texture.id);
}
MORE INFO: The rendering is the simple vanilla forward rendering.
400 meshes and 2 million triangles
400 different meshes? If yes, then simplify your model, that's ridiculous. If no, then use instancing. The 400 draw calls is just as ridiculous.
however when I make the window about the size of my screen (2560x1600), performance hugely drops to about 15-20 fps
While the overall architecture of your program is near unsalvageable, that sentence indicates that at least part of the problem is that you're running into your graphics card's fill rate limit (at least ignoring the large amounts of time it's doing nothing because you're too busy looping through random objects, drawing them one by one).

My OpenGL program stops running when I use the Assimp importer.ReadFile

I'd like to ask a question about assimp. When I try to run my code the compiler and the linker give me no errors but it crashes when I use the ReadLine function, but if I comment the part in which I use it the program works fine.
Here's the model.cpp file:
#include "Model.h"
Model::Model(GLchar *path)
{
this->LoadModel(path);
}
Model::~Model() {
}
void Model::Draw(Shader shader)
{
for (GLuint i = 0; i < this->meshes.size(); i++)
{
this->meshes[i].Draw(shader);
}
}
void Model::LoadModel(string path)
{
// Read file via ASSIMP
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
// Check for errors
if (!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero
{
cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl;
return;
}
// Retrieve the directory path of the filepath
this->directory = path.substr(0, path.find_last_of('/'));
// Process ASSIMP's root node recursively
this->processNode(scene->mRootNode, scene);
}
// Processes a node in a recursive fashion. Processes each individual mesh located at the node and repeats this process on its children nodes (if any).
void Model::processNode(aiNode* node, const aiScene* scene)
{
// Process each mesh located at the current node
for (GLuint i = 0; i < node->mNumMeshes; i++)
{
// The node object only contains indices to index the actual objects in the scene.
// The scene contains all the data, node is just to keep stuff organized (like relations between nodes).
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
this->meshes.push_back(this->processMesh(mesh, scene));
}
// After we've processed all of the meshes (if any) we then recursively process each of the children nodes
for (GLuint i = 0; i < node->mNumChildren; i++)
{
this->processNode(node->mChildren[i], scene);
}
}
Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene)
{
// Data to fill
vector<Vertex> vertices;
vector<GLuint> indices;
vector<Texture> textures;
// Walk through each of the mesh's vertices
for (GLuint i = 0; i < mesh->mNumVertices; i++)
{
Vertex vertex;
glm::vec3 vector; // We declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to glm's vec3 class so we transfer the data to this placeholder glm::vec3 first.
// Positions
vector.x = mesh->mVertices[i].x;
vector.y = mesh->mVertices[i].y;
vector.z = mesh->mVertices[i].z;
vertex.Position = vector;
// Normals
vector.x = mesh->mNormals[i].x;
vector.y = mesh->mNormals[i].y;
vector.z = mesh->mNormals[i].z;
vertex.Normal = vector;
// Texture Coordinates
if (mesh->mTextureCoords[0]) // Does the mesh contain texture coordinates?
{
glm::vec2 vec;
// A vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't
// use models where a vertex can have multiple texture coordinates so we always take the first set (0).
vec.x = mesh->mTextureCoords[0][i].x;
vec.y = mesh->mTextureCoords[0][i].y;
vertex.TexCoords = vec;
}
else
{
vertex.TexCoords = glm::vec2(0.0f, 0.0f);
}
vertices.push_back(vertex);
}
// Now wak through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices.
for (GLuint i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];
// Retrieve all indices of the face and store them in the indices vector
for (GLuint j = 0; j < face.mNumIndices; j++)
{
indices.push_back(face.mIndices[j]);
}
}
// Process materials
if (mesh->mMaterialIndex >= 0)
{
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
// We assume a convention for sampler names in the shaders. Each diffuse texture should be named
// as 'texture_diffuseN' where N is a sequential number ranging from 1 to MAX_SAMPLER_NUMBER.
// Same applies to other texture as the following list summarizes:
// Diffuse: texture_diffuseN
// Specular: texture_specularN
// Normal: texture_normalN
// 1. Diffuse maps
vector<Texture> diffuseMaps = this->LoadMaterialTexture(material, aiTextureType_DIFFUSE, "texture_diffuse");
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
// 2. Specular maps
vector<Texture> specularMaps = this->LoadMaterialTexture(material, aiTextureType_SPECULAR, "texture_specular");
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
}
// Return a mesh object created from the extracted mesh data
return Mesh(vertices, indices, textures);
}
// Checks all material textures of a given type and loads the textures if they're not loaded yet.
// The required info is returned as a Texture struct.
vector<Texture> Model::LoadMaterialTexture(aiMaterial *mat, aiTextureType type, string typeName)
{
vector<Texture> textures;
for (GLuint i = 0; i < mat->GetTextureCount(type); i++)
{
aiString str;
mat->GetTexture(type, i, &str);
// Check if texture was loaded before and if so, continue to next iteration: skip loading a new texture
GLboolean skip = false;
for (GLuint j = 0; j < textures_loaded.size(); j++)
{
if (textures_loaded[j].path == str)
{
textures.push_back(textures_loaded[j]);
skip = true; // A texture with the same filepath has already been loaded, continue to next one. (optimization)
break;
}
}
if (!skip)
{ // If texture hasn't been loaded already, load it
Texture texture;
texture.id = TextureFromFile(str.C_Str(), this->directory);
texture.type = typeName;
texture.path = str;
textures.push_back(texture);
this->textures_loaded.push_back(texture); // Store it as texture loaded for entire model, to ensure we won't unnecesery load duplicate textures.
}
}
return textures;
}
GLint TextureFromFile(const char *path, string directory)
{
//Generate texture ID and load texture data
string filename = string(path);
filename = directory + '/' + filename;
GLuint textureID;
glGenTextures(1, &textureID);
int width, height;
unsigned char *image = SOIL_load_image(filename.c_str(), &width, &height, 0, SOIL_LOAD_RGB);
// Assign texture to ID
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
// Parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
SOIL_free_image_data(image);
return textureID;
}
The part of Model.cpp that causes the problem is this one:
const aiScene *scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
// Check for errors
if (!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero
{
cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl;
return;
}
// Retrieve the directory path of the filepath
this->directory = path.substr(0, path.find_last_of('/'));
// Process ASSIMP's root node recursively
this->processNode(scene->mRootNode, scene);
The part of the main in which I create my model object:
Model ourModel("Object/models/nanosuit.obj");
I've been searching for a solution for an entire day. If someone could help me figure it out I would be really glad. If you need other parts of the code please tell me.
At first please check if this is an error in our model-file. You can use the Assimp-Viewer for importing the model. If you can see it there this is a sign that your code is broken.
If the model-loader shows you an error the importer or the model could be corrupt. If this is the case: just reopen the issue on the Asset-Importer-Lib project page ( see https://github.com/assimp/assimp/issues/1215 ).

OpenGL Model/Texture rendering using VAO/VBO

I am trying to render 3D models with textures using Assimp. The conversion goes perfect, all textures positions and what not gets loaded. I have tested the texture images by drawing them to the screen in 2D.
For some reason it does not render the textures to the model.
I am a beginner in OpenGL so forgive me if i dont explain it right.
The tutorial I have based the code on is from here, but i stripped a big part since I have my own camera/movement system.
The model renders like this: http://i.stack.imgur.com/5sK9K.png
whilest the texture in use looks like this: http://i.stack.imgur.com/sWGp7.jpg
The relevant rendering code is the following:
Generating textures from data file:
int Mesh::LoadGLTextures(const aiScene* scene){
if (scene->HasTextures()) return -1; //yes this is correct
/* getTexture Filenames and Numb of Textures */
for (unsigned int m = 0; m<scene->mNumMaterials; m++){
int texIndex = 0;
aiReturn texFound;
aiString path; // filename
while ((texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path)) == AI_SUCCESS){
textureIdMap[path.data] = NULL; //fill map with textures, pointers still NULL yet
texIndex++;
}
}
int numTextures = textureIdMap.size();
/* create and fill array with GL texture ids */
GLuint* textureIds = new GLuint[numTextures];
/* get iterator */
std::map<std::string, GLuint>::iterator itr = textureIdMap.begin();
std::string basepath = getBasePath(path);
ALLEGRO_BITMAP *image;
for (int i = 0; i<numTextures; i++){
std::string filename = (*itr).first; // get filename
(*itr).second = textureIds[i]; // save texture id for filename in map
itr++; // next texture
std::string fileloc = basepath + filename; /* Loading of image */
image = al_load_bitmap(fileloc.c_str());
if (image) /* If no error occured: */{
GLuint texId = al_get_opengl_texture(image);
//glGenTextures(numTextures, &textureIds[i]); /* Texture name generation */
glBindTexture(GL_TEXTURE_2D, texId); /* Binding of texture name */
//redefine standard texture values
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* We will use linear
interpolation for magnification filter */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); /* We will use linear
interpolation for minifying filter */
textureIdMap[filename] = texId;
} else {
/* Error occured */
std::cout << "Couldn't load Image: " << fileloc.c_str() << "\n";
}
}
//Cleanup
delete[] textureIds;
//return success
return true;
}
Generating VBO/VAO:
void Mesh::genVAOsAndUniformBuffer(const aiScene *sc) {
struct MyMesh aMesh;
struct MyMaterial aMat;
GLuint buffer;
// For each mesh
for (unsigned int n = 0; n < sc->mNumMeshes; ++n){
const aiMesh* mesh = sc->mMeshes[n];
// create array with faces
// have to convert from Assimp format to array
unsigned int *faceArray;
faceArray = (unsigned int *)malloc(sizeof(unsigned int) * mesh->mNumFaces * 3);
unsigned int faceIndex = 0;
for (unsigned int t = 0; t < mesh->mNumFaces; ++t) {
const aiFace* face = &mesh->mFaces[t];
memcpy(&faceArray[faceIndex], face->mIndices, 3 * sizeof(unsigned int));
faceIndex += 3;
}
aMesh.numFaces = sc->mMeshes[n]->mNumFaces;
// generate Vertex Array for mesh
glGenVertexArrays(1, &(aMesh.vao));
glBindVertexArray(aMesh.vao);
// buffer for faces
glGenBuffers(1, &buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * mesh->mNumFaces * 3, faceArray, GL_STATIC_DRAW);
// buffer for vertex positions
if (mesh->HasPositions()) {
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * mesh->mNumVertices, mesh->mVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(vertexLoc);
glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0);
}
// buffer for vertex normals
if (mesh->HasNormals()) {
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * mesh->mNumVertices, mesh->mNormals, GL_STATIC_DRAW);
glEnableVertexAttribArray(normalLoc);
glVertexAttribPointer(normalLoc, 3, GL_FLOAT, 0, 0, 0);
}
// buffer for vertex texture coordinates
if (mesh->HasTextureCoords(0)) {
float *texCoords = (float *)malloc(sizeof(float) * 2 * mesh->mNumVertices);
for (unsigned int k = 0; k < mesh->mNumVertices; ++k) {
texCoords[k * 2] = mesh->mTextureCoords[0][k].x;
texCoords[k * 2 + 1] = mesh->mTextureCoords[0][k].y;
}
glGenBuffers(1, &buffer);
glEnableVertexAttribArray(texCoordLoc);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 2 * mesh->mNumVertices, texCoords, GL_STATIC_DRAW);
glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);
}
// unbind buffers
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// create material uniform buffer
aiMaterial *mtl = sc->mMaterials[mesh->mMaterialIndex];
aiString texPath; //contains filename of texture
if (AI_SUCCESS == mtl->GetTexture(aiTextureType_DIFFUSE, 0, &texPath)){
//bind texture
unsigned int texId = textureIdMap[texPath.data];
aMesh.texIndex = texId;
aMat.texCount = 1;
} else {
aMat.texCount = 0;
}
float c[4];
set_float4(c, 0.8f, 0.8f, 0.8f, 1.0f);
aiColor4D diffuse;
if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &diffuse))
color4_to_float4(&diffuse, c);
memcpy(aMat.diffuse, c, sizeof(c));
set_float4(c, 0.2f, 0.2f, 0.2f, 1.0f);
aiColor4D ambient;
if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &ambient))
color4_to_float4(&ambient, c);
memcpy(aMat.ambient, c, sizeof(c));
set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
aiColor4D specular;
if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &specular))
color4_to_float4(&specular, c);
memcpy(aMat.specular, c, sizeof(c));
set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
aiColor4D emission;
if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_EMISSIVE, &emission))
color4_to_float4(&emission, c);
memcpy(aMat.emissive, c, sizeof(c));
float shininess = 0.0;
unsigned int max;
aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS, &shininess, &max);
aMat.shininess = shininess;
glGenBuffers(1, &(aMesh.uniformBlockIndex));
glBindBuffer(GL_UNIFORM_BUFFER, aMesh.uniformBlockIndex);
glBufferData(GL_UNIFORM_BUFFER, sizeof(aMat), (void *)(&aMat), GL_STATIC_DRAW);
myMeshes.push_back(aMesh);
}
}
Rendering model:
void Mesh::recursive_render(const aiScene *sc, const aiNode* nd){
// draw all meshes assigned to this node
for (unsigned int n = 0; n < nd->mNumMeshes; ++n){
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, myMeshes[nd->mMeshes[n]].texIndex);
// bind VAO
glBindVertexArray(myMeshes[nd->mMeshes[n]].vao);
// draw
glDrawElements(GL_TRIANGLES, myMeshes[nd->mMeshes[n]].numFaces * 3, GL_UNSIGNED_INT, 0);
}
// draw all children
for (unsigned int n = 0; n < nd->mNumChildren; ++n){
recursive_render(sc, nd->mChildren[n]);
}
}
Any other relevant code parts can be found in my open github project https://github.com/kwek20/StrategyGame/tree/master/Strategy
Mesh.cpp is relevant, as well as main.cpp and Camera.cpp.
As far as I understaind I followed the guidelines well, created a VAO, created VBOs, added data and enabled the proper vertex array attriute tot render the scene with.
I have checked all the data variables and everything is filled according to plan
Could anyone here spot the mistake I have made and or explain it?
Some links are typed weird because of the limit I have :(
It would help if you posted your shaders also.
I can post some rendering code with textures if that helps you out:
Generating the texture for opengl and loading a grayscale (UC8) image with width and height into the GPU
void GLRenderer::getTexture(unsigned char * image, int width, int height)
{
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &mTextureID);
glBindTexture(GL_TEXTURE_2D, mTextureID);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, width, height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, image);
if (aux::checkGlErrors(__LINE__, __FILE__))assert(false);
glBindTexture(GL_TEXTURE_2D, 0);
}
Loading the vertices from assimp onto the gpu
//** buffer a obj file-style model, initialize the VAO
void GLRenderer::bufferModel(float* aVertexArray, int aNumberOfVertices, float* aNormalArray, int aNumberOfNormals, float* aUVList, int aNumberOfUVs, unsigned int* aIndexList, int aNumberOfIndices)
{
//** just to be sure we are current
glfwMakeContextCurrent(mWin);
//** Buffer all data in VBOs
glGenBuffers(1, &mVertex_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, mVertex_buffer_object);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * aNumberOfVertices * 3, aVertexArray, GL_STATIC_DRAW);
glGenBuffers(1, &mNormal_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, mNormal_buffer_object);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * aNumberOfNormals * 3, aNormalArray, GL_STATIC_DRAW);
glGenBuffers(1, &mUV_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, mUV_buffer_object);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * aNumberOfUVs * 2, aUVList, GL_STATIC_DRAW);
glGenBuffers(1, &mIndex_buffer_object);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndex_buffer_object);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * aNumberOfIndices, aIndexList, GL_STATIC_DRAW);
if (aux::checkGlErrors(__LINE__, __FILE__))assert(false);
//** VAO tells our shaders how to match up data from buffer to shader input variables
glGenVertexArrays(1, &mVertex_array_object);
glBindVertexArray(mVertex_array_object);
//** vertices first
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, mVertex_buffer_object);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
//** normals next
if (aNumberOfNormals > 0){
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, mNormal_buffer_object);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
}
//** UVs last
if (aNumberOfUVs > 0){
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, mUV_buffer_object);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, NULL);
}
//** indexing for reusing vertices in triangle-meshes
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndex_buffer_object);
//** check errors and store the number of vertices
if (aux::checkGlErrors(__LINE__, __FILE__))assert(false);
mNumVert = aNumberOfVertices;
mNumNormals = aNumberOfNormals;
mNumUVs = aNumberOfUVs;
mNumIndices = aNumberOfIndices;
}
The code above is called like:
//read vertices from file
std::vector<float> vertex, normal, uv;
std::vector<unsigned int> index;
//assimp-wrapping function to load obj to vectors
aux::loadObjToVectors("Resources\\vertices\\model.obj", vertex, normal, index, uv);
mPtr->bufferModel(&vertex[0], static_cast<int>(vertex.size()) / 3, &normal[0], static_cast<int>(normal.size()) / 3, &uv[0], static_cast<int>(uv.size()) / 2, &index[0], static_cast<int>(index.size()));
Then comes the shader-part:
In the vertex shader you just hand-through the UV-coordinate layer
#version 400 core
layout (location = 0) in vec3 vertexPosition_modelspace;
layout (location = 1) in vec3 vertexNormal_modelspace;
layout (location = 2) in vec2 vertexUV;
out vec2 UV;
[... in main then ...]
UV = vertexUV;
While in the fragment shader you assign the value to the pixel:
#version 400 core
in vec2 UV;
uniform sampler2D textureSampler;
layout(location = 0) out vec4 outColor;
[... in main then ...]
// you probably want to calculate lighting here then too, so its just the simplest way to get the texture inside
outColor = vec4(texture2D(textureSampler, UV).rgb, cosAngle);
//you can also check whether the UV coords are correctly bound by using:
outColor = vec4(UV.x, UV.y,1,1);
//and then checking the pixel-values in the resulting image (e.g. render it to a PBO and then download it onto the CPU for)
In the rendering loop also make sure that all the uniforms are correctly bound (especially texture related ones) and that the texture is active and bound
if (mTextureID != -1) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTextureID);
}
GLint textureLocation = glGetUniformLocation(mShaderProgram, "textureSampler");
glUniform1i(textureLocation, 0);
//**set the poligon mode
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//**drawElements because of indexing
glDrawElements(GL_TRIANGLES, mNumIndices, GL_UNSIGNED_INT, 0);
I hope I could help you!
Kind regards,
VdoP

Using glDrawElements does not draw my .obj file

I am trying to correctly import an .OBJ file from 3ds Max. I got this working using glBegin() & glEnd() from a previous question on here, but had really poor performance obviously, so I am trying to use glDrawElements now.
I am importing a chessboard, its game pieces, etc. The board, each game piece, and each square on the board is stored in a struct GroupObject. The way I store the data is like this:
struct Vertex
{
float position[3];
float texCoord[2];
float normal[3];
float tangent[4];
float bitangent[3];
};
struct Material
{
float ambient[4];
float diffuse[4];
float specular[4];
float shininess; // [0 = min shininess, 1 = max shininess]
float alpha; // [0 = fully transparent, 1 = fully opaque]
std::string name;
std::string colorMapFilename;
std::string bumpMapFilename;
std::vector<int> indices;
int id;
};
//A chess piece or square
struct GroupObject
{
std::vector<Material *> materials;
std::string objectName;
std::string groupName;
int index;
};
All vertices are triangles, so there are always 3 points. When I am looping through the faces f section in the obj file, I store the v0, v1, & v2 in the Material->indices. (I am doing v[0-2] - 1 to account for obj files being 1-based and my vectors being 0-based.
So when I get to the render method, I am trying to loop through every object, which loops through every material attached to that object. I set the material information and try and use glDrawElements. However, the screen is black. I was able to draw the model just fine when I looped through each distinct material with all the indices associated with that material, and it drew the model fine. This time around, so I can use the stencil buffer for selecting GroupObjects, I changed up the loop, but the screen is black.
UPDATE
Replaced original render loop with current one and screenshot of it's result
Here is my render loop. The only thing I changed was the for loop(s) so they go through each object, and each material in the object in turn.
void GLEngine::drawModel()
{
ModelTextures::const_iterator iter;
GLuint texture = 0;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Vertex arrays setup
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer(3, GL_FLOAT, model.getVertexSize(), model.getVertexBuffer()->position);
glEnableClientState( GL_NORMAL_ARRAY );
glNormalPointer(GL_FLOAT, model.getVertexSize(), model.getVertexBuffer()->normal);
glClientActiveTexture( GL_TEXTURE0 );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glTexCoordPointer(2, GL_FLOAT, model.getVertexSize(), model.getVertexBuffer()->texCoord);
glUseProgram(blinnPhongShader);
objects = model.getObjects();
// Loop through objects...
for( int i=0 ; i < objects.size(); ++i )
{
ModelOBJ::GroupObject *object = objects[i];
// Loop through materials used by object...
for( int j=0 ; j<object->materials.size() ; ++j )
{
ModelOBJ::Material *pMaterial = object->materials[j];
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pMaterial->ambient);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pMaterial->diffuse);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, pMaterial->specular);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pMaterial->shininess * 128.0f);
if (pMaterial->bumpMapFilename.empty())
{
//Bind the color map texture.
texture = nullTexture;
if (enableTextures)
{
iter = modelTextures.find(pMaterial->colorMapFilename);
if (iter != modelTextures.end())
texture = iter->second;
}
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
//Update shader parameters.
glUniform1i(glGetUniformLocation(
blinnPhongShader, "colorMap"), 0);
glUniform1f(glGetUniformLocation(
blinnPhongShader, "materialAlpha"), pMaterial->alpha);
}
//glDrawElements( GL_TRIANGLES, pMaterial->triangleCount * 3, GL_UNSIGNED_INT, &pMaterial->indices.front() );
glDrawElements( GL_TRIANGLES, pMaterial->triangleCount * 3, GL_UNSIGNED_INT, model.getIndexBuffer() + pMaterial->startIndex );
}
}
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glDisable(GL_BLEND);
}
Here is what the above method draws:
http://img844.imageshack.us/img844/3793/chess4.png
I don't know what I am missing that's important. If it's also helpful, here is where I read a 'f' face line and store the info in the obj importer in the pMaterial->indices.
else if (sscanf(buffer, "%d/%d/%d", &v[0], &vt[0], &vn[0]) == 3) // v/vt/vn
{
fscanf(pFile, "%d/%d/%d", &v[1], &vt[1], &vn[1]);
fscanf(pFile, "%d/%d/%d", &v[2], &vt[2], &vn[2]);
v[0] = (v[0] < 0) ? v[0] + numVertices - 1 : v[0] - 1;
v[1] = (v[1] < 0) ? v[1] + numVertices - 1 : v[1] - 1;
v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1;
currentMaterial->indices.push_back(v[0]);
currentMaterial->indices.push_back(v[1]);
currentMaterial->indices.push_back(v[2]);
UPDATE 2
Current output: http://img337.imageshack.us/img337/860/chess4s.png
I was able to fix the model with the following code
glDrawElements( GL_TRIANGLES, pMaterial->triangleCount * 3, GL_UNSIGNED_INT, model.getIndexBuffer() + pMaterial->startIndex );
When I was done importing the model, I went through running a triangleCount & set the startIndex like so. This was my solution:
for (int i = 0; i < static_cast<int>(m_attributeBuffer.size()); i++)
{
if (m_attributeBuffer[i] != materialId)
{
materialId = m_attributeBuffer[i];
++numMaterials;
}
}
// Allocate memory for the materials and reset counters.
m_numberOfObjectMaterials = numMaterials;
m_materials.resize(m_numberOfObjectMaterials);
numMaterials = 0;
materialId = -1;
// Build the meshes. One mesh for each unique material.
for (int i = 0; i < static_cast<int>(m_attributeBuffer.size()); i++)
{
if (m_attributeBuffer[i] != materialId)
{
materialId = m_attributeBuffer[i];
m = m_ObjectMaterials[materialId];
m->startIndex = i * 3;
m->triangleCount = 0;
++m->triangleCount;
}
else
{
++m->triangleCount;
}
}