I am using glBufferSubData to change positions of vertices.
Everything works fine, when I call the code below once.
If I run this whole code two times to add two pointclouds the vertex positions is updated only for the last geometry.
Please look at only following methods of the class, because the rest of the code is just a constructor to initialize the clouds:
void opengl_init()
void opengl_draw()
I think something is wrong with winding to the vao buffer.
Question:
How can I correctly update vertex positions glBufferSubData so that both pointclouds would move?
class pointcloud {
public:
int id = 0;
std::vector<vertex> vertices;
std::vector<unsigned int> indices;//pairs
unsigned int vao, vbo, ibo;
//individual polylines
pointcloud(const std::vector<float>& coord, const std::vector<float>& colors)//, std::vector<Texture> textures
{
vertices.reserve(coord.size());
indices.reserve((coord.size() - 1) * 2);
for (int i = 0; i < coord.size(); i += 3) {
vertex vertex;
vertex.position = glm::vec3(coord[i + 0], coord[i + 1], coord[i + 2]);
vertex.color = glm::vec3(colors[i + 0], colors[i + 1], colors[i + 2]);
vertices.emplace_back(vertex);
}
for (int i = 0; i < (coord.size() / 3) - 1; i++) {
indices.emplace_back(i + 0);
indices.emplace_back(i + 1);
}
// now that we have all the required data, set the vertex buffers and its attribute pointers.
//setup_polyline();
}
//merged polylines
pointcloud(const std::vector<std::vector<float>>& coord, const std::vector<std::vector<float>>& colors)//, std::vector<Texture> textures
{
//reserve memory
int v_count = 0;
int i_count = 0;
for (int i = 0; i < coord.size(); i++) {
v_count += coord[i].size();
i_count += coord[i].size() - 1;
}
vertices.reserve(v_count);
indices.reserve(i_count);
//fill vertics and indices lists
for (int i = 0; i < coord.size(); i++) {
for (int j = 0; j < coord[i].size(); j += 3) {
vertex vertex;
vertex.position = glm::vec3(coord[i][j + 0], coord[i][j + 1], coord[i][j + 2]);
vertex.color = glm::vec3(colors[i][j + 0], colors[i][j + 1], colors[i][j + 2]);
vertices.emplace_back(vertex);
}
}
v_count = 0;
for (int i = 0; i < coord.size(); i++) {
for (int j = 0; j < (coord[i].size() / 3) - 1; j++) {
//std::cout << v_count + j + 0 << " " << v_count + j + 1 << std::endl;
indices.emplace_back(v_count + j + 0);
indices.emplace_back(v_count + j + 1);
}
v_count += (coord[i].size() / 3);
}
//std::cout << vertices.size() << std::endl;
// now that we have all the required data, set the vertex buffers and its attribute pointers.
//setup_polyline();
}
//// initializes all the buffer objects/arrays
void opengl_init(bool draw_dynamic = true, int _id = 0)
{
id = _id + 1;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// init vertex-array and vertex-array-buffer
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//vertex array
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
//bind vertex-array-buffer to the vertex-array
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//declare array with data or empty array depending how data will be displayed
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
auto type = !draw_dynamic ? GL_STATIC_DRAW : GL_STREAM_DRAW;
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vertex), &vertices[0], type); // target | size | data (poinnting to first element e.g. glm::value_ptr(vertices[0])) | usage
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// set attributes that corresponds to layout id in the vertex shader
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// vertex Positions
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (void*)0);
// vertex normals
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (void*)offsetof(vertex, color));
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//bind buffers vao | vbo | ibo
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//glBindVertexArray(0);
}
// render the mesh
void opengl_draw(opengl_shaders::shader& shader, bool draw_dynamic = true)
{
//update
//https://learnopengl.com/Advanced-OpenGL/Advanced-Data
if (draw_dynamic) {
for (auto& v : vertices)
v.position.y += 0.001;
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(vertex), &vertices[0]);
}
//draw
glBindVertexArray(vao);
glDrawArrays(GL_POINTS, 0, vertices.size());
//glBindVertexArray(0);
}
void opengl_clear(opengl_shaders::shader& shader) {
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
shader.delete_shader();
}
};
glBufferSubData updates a subset of the data store of a buffer object that is currently bound to the specified target. You must bind the buffer before you can modify its data:
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(vertex), &vertices[0]);
The GL_ARRAY_BUFFER binding is a global state. This binding is maintained until it is changed. As for your code it works for 1 pointcloud, but it doesn't work if you have more than 1 pointcloud and vertex buffer.
Related
I need to generate procedural terrain using Noise (using Perlin noise) in OpenGL. Each time the application runs a new terrain, it needs to be generated using a new seed. (Do not use external library.) Is there a method/requirement needed when making a class for noise terrains. What functions/calculation i need to call and in which order ?
PS: I use Visual Studio 2019.
// Copy the array data into a float array, and scale and offset the heights.
mHeightmap.resize(NumRows * NumCols, 0);
for( int i = 0; i < NumRows * NumCols; ++i)
{
mHeightmap[i] = (float)in[i] * HeightScale;
}
// A height for each vertex
{
std::vector<unsigned char> in(NumRows * NumCols);
// Open the file.
std::ifstream inFile;
inFile.open(heightmapName.c_str(), std::ios_base::binary);
if (inFile)
{
// Read the RAW bytes.
inFile.read((char*)&in[0], (std::streamsize)in.size());
// Done with file.
inFile.close();
}
// Copy the array data into a float array, and scale and offset the heights.
mHeightmap.resize(NumRows * NumCols, 0);
for( int i = 0; i < NumRows * NumCols; ++i)
{
mHeightmap[i] = (float)in[i] * HeightScale;
}
void Terrain::CreateVAO()
{
std::vector<GLfloat> vertices;
vertices.reserve(NumCols * NumRows * 8);
float invTwoDX = 1.0f / (2.0f * CellSpacing);
float invTwoDZ = 1.0f / (2.0f * CellSpacing);
//vertices
for ( int z = 0; z < NumRows; z++)
{
for ( int x = 0; x < NumCols; x++)
{
//vertex data
int i = z * NumCols + x;
vertices.push_back((float)x*CellSpacing);
vertices.push_back(mHeightmap[i]);
vertices.push_back((float)z * CellSpacing);
//normal data
glm::vec3 _N = { 0.0f,1.0f, 0.0f };
if(z >= 1 && z < NumRows -1 && x >= 1 && z < NumCols - 1)
{
float t = mHeightmap[(z - 1) * NumCols + x];
float b = mHeightmap[(z + 1) * NumCols + x];
float l = mHeightmap[z * NumCols + x - 1];
float r = mHeightmap[z * NumCols + x + 1];
glm::vec3 tanZ(0.0f, (b - t) * invTwoDZ, 1.0f);
glm::vec3 tanX(1.0f, (r - l) * invTwoDX, 0.0f);
glm::vec3 _C, _N;
_C = glm::cross(tanZ, tanX);
_N = glm::normalize(_C);
}
vertices.push_back(_N.x);
vertices.push_back(_N.y);
vertices.push_back(_N.z);
vertices.push_back((float)x);
vertices.push_back((float)z);
}
}
std::vector<GLuint> indices;
vertices.reserve((NumCols-1)*(NumRows -1)*6);
//indices
for ( int z = 0; z < NumRows-1; z++)
{
for ( int x = 0; x < NumCols-1; x++)
{
GLint a = z * NumCols + x;
GLint b = (z +1) * NumCols + x;
GLint c = z * NumCols + (x+1);
GLint d = (z+1) * NumCols + (x+1);
indices.push_back(c);
indices.push_back(a);
indices.push_back(b);
indices.push_back(c);
indices.push_back(b);
indices.push_back(d);
}
}
indexcount = indices.size();
GLuint VBO, EBO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(GLfloat), vertices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(
0,
3,
GL_FLOAT,
GL_FALSE,
8 * sizeof(GLfloat), //Strude of the single vertex(pos)
(GLvoid*)0); //Offset from beginning of Vertex
glEnableVertexAttribArray(0);
glVertexAttribPointer(
1,
3,
GL_FLOAT,
GL_FALSE,
8 * sizeof(GLfloat), //Strude of the single vertex(pos+color)
(GLvoid*)(3 * sizeof(GLfloat))); //Offset from beginning of Vertex
glEnableVertexAttribArray(1);
glVertexAttribPointer(
2,
2, //2 float component for coordinates
GL_FLOAT,
GL_FALSE,
8 * sizeof(GLfloat), //Strude of the single vertex(pos+color+texture)
(GLvoid*)(6 * sizeof(GLfloat)));//Offset from beginning of Vertex
glEnableVertexAttribArray(2);
I'm not sure if I see usage of Perlin noise in your code. Try this lightweight, easy to integrate library:
https://github.com/Auburn/FastNoise which has Perlin and tons of other useful stuff like a visualizer.
Usage is as simple as
noise.GetNoise((float)x, (float)y); which you can plug into your height function
I have a problem copying/upload all the data into the buffer using glBufferSubData(). I want to copy chunk by chunk to the buffer. So I have used this approach. I am seeing a blank screen when I try to render. Please find my code below. do you see any problem in calculating the buffer offset? Or is this not the way to copy data into the buffer?
Below is the data structure
struct DisplayIndexID {
int idx;
DrawStateT drawState;
//Every display Index ID has its own draw models.
std::vector<std::unique_ptr<vertexModel>> readytoDrawModels;
};
void initVboData(std::vector<DisplayIndexID> & v)
{
glBindBuffer(GL_ARRAY_BUFFER, geomVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(QVector3D) * 4096, NULL, GL_DYNAMIC_DRAW);
std::vector<QVector3D> vecToDraw;
GLintptr offset = 0;
for (int i = 0; i < v.size(); i++)
for (auto& vModel : v[i].readytoDrawModels)
{
if (vModel) {
vecToDraw = vModel->getVertices();
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(QVector3D) * vecToDraw.size(), &vecToDraw[0]);
offset += sizeof(QVector3D) * vecToDraw.size(); // is this offset calculation fine?
}
}
}
//Below is my draw function
void drawDisplayLists(std::vector<DisplayIndexID> & v)
{
initVboData(v);
for (int i = 0; i < v.size(); i++)
{
//Make context current
makeCurrent();
bool isTextureUsed = false;
//Apply Projection Matrix.
GLint mvp_mat = 0;
GLint mvp_matText = 0;
///***********PRINT AREA***********************/
for (auto& vModel : v[i].readytoDrawModels)
{
// Code related to Shaders
......
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(QVector3D), nullptr); how to get the offset and add offset here.
switch (vModel->getDrawMode())
{
case 0: //GL_POINTS
glDrawArrays(GL_POINTS, 0, vModel->getVertices().size());
break;
case 1: //GL_LINES
glDrawArrays(GL_LINES, 0, vModel->getVertices().size());
break;
case 2: //GL_LINE_LOOP
glDrawArrays(GL_LINE_LOOP, 0, vModel->getVertices().size());
break;
}
}
}
}
Considering that you have persistent VBO initialized like that:
GLintptr offset = 0;
for (int i = 0; i < v.size(); i++) {
for (auto& vModel : v[i].readytoDrawModels) {
if (vModel) {
vecToDraw = vModel->getVertices();
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(QVector3D) * vecToDraw.size(), &vecToDraw[0]);
offset += sizeof(QVector3D) * vecToDraw.size();
}
}
}
then you should replicate the same offsets within drawing loop.
This can be done either by passing offset in bytes to glVertexAttribPointer() computed exactly in the same way as during VBO initialization:
GLintptr offset = 0;
for (int i = 0; i < v.size(); i++) {
for (auto& vModel : v[i].readytoDrawModels) {
if (vModel) {
vecToDraw = vModel->getVertices();
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(QVector3D), offset);
glDrawArrays(GL_LINES, 0, vecToDraw.size());
offset += sizeof(QVector3D) * vecToDraw.size();
}
}
}
or by passing offset in number of vertices to glDrawArrays(), in case if all models have the same vertex definition as in your code snippet (in vec3 Position):
GLintptr aFirstIndex = 0;
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(QVector3D), nullptr);
for (int i = 0; i < v.size(); i++) {
for (auto& vModel : v[i].readytoDrawModels) {
if (vModel) {
vecToDraw = vModel->getVertices();
glDrawArrays(GL_LINES, aFirstIndex, vecToDraw.size());
aFirstIndex += vecToDraw.size();
}
}
}
Make sure that allocated VBO has a proper size and 4096 is replaced by a real size:
glBufferData(GL_ARRAY_BUFFER, sizeof(QVector3D) * 4096, NULL, GL_DYNAMIC_DRAW);
By the way, grouping models by GL_POINTS/GL_LINES array type and same presentation attributes (color/material/etc.) would allow rendering each group with a single glDrawArrays() call, instead of numerous glDrawArrays() for every model, which would be more optimal from performance point of view.
this is the code I use to create and draw an ellipsoid with OpenGL with shader
const float _2pi = 2.0f * M_PI;
std::vector<glm::vec3> positions;
std::vector<glm::vec3> normals;
std::vector<glm::vec2> textureCoords;
for(int i = 0; i <= stacks; ++i) {
// V texture coordinate
float V = i / (float)stacks;
float phi = V * M_PI;
for( int j = 0; j <= slices; ++j) {
// U texture coordinate
float U = j / (float)slices;
float theta = U * _2pi;
float X = a * cos(theta) * cos(phi);
float Y = b * cos(theta) * sin(phi);
float Z = c * sin(theta);
positions.push_back( glm::vec3( X, Y, Z) );
normals.push_back( glm::vec3(X, Y, Z) );
textureCoords.push_back( glm::vec2(U, V) );
}
}
// Now generate the index buffer
std::vector<GLuint> indicies;
for(int i=0; i <slices*stacks+slices; ++i) {
indicies.push_back(i);
indicies.push_back(i + slices + 1);
indicies.push_back(i + slices);
indicies.push_back(i + slices + 1);
indicies.push_back(i);
indicies.push_back(i + 1);
}
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(4, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(glm::vec3), positions.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), normals.data(), GL_STATIC_DRAW);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_TRUE, 0, nullptr);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ARRAY_BUFFER, textureCoords.size() * sizeof(glm::vec2), textureCoords.data(), GL_STATIC_DRAW);
glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(8);
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glBufferData( GL_ELEMENT_ARRAY_BUFFER, indicies.size() * sizeof(GLuint), indicies.data(), GL_STATIC_DRAW);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
while this is the code that I use to render it:
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
if(style == glObject::STYLE::WIREFRAME) glDrawElements(GL_LINES, (slices * stacks + slices) * 6, GL_UNSIGNED_INT, nullptr);
if(style == glObject::STYLE::SOLID) glDrawElements(GL_TRIANGLES, (slices * stacks + slices) * 6, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
It seems to work but I have some issue.
Looking the image it is possible to see some vertex in the wrong position.
I think that is something related to the indicies but I'm not sure.
I have notice that depends by the number of stacks or slices that I use
UPDATE:
I take into account the suggestion of #Rabbid76 and this is the result.
No more degenerated vertex and triangles in the rendering.
However the rendering is not equal to the one of #Rabbid76 there is like a rotation of the vertex.
FINAL:
This is the creation vertex and indices code:
std::vector<glm::vec3> positions;
std::vector<glm::vec3> normals;
std::vector<glm::vec2> textureCoords;
for(int i = 0; i <= stacks; ++i) {
// V texture coordinate.
float V = i / (float)stacks;
float phi = V * M_PI;
for( int j = 0; j <= slices; ++j) {
// U texture coordinate.
float U = j / (float)slices;
float theta = U * 2.0f * M_PI;
float X = cos(theta) * sin(phi);
float Y = cos(phi);
float Z = sin(theta) * sin(phi);
positions.push_back( glm::vec3( X, Y, Z) * radius );
normals.push_back( glm::vec3(X, Y, Z) );
textureCoords.push_back( glm::vec2(U, V) );
}
}
// Now generate the index buffer
std::vector<GLuint> indicies;
int noPerSlice = slices + 1;
for(int i=0; i < stacks; ++i) {
for (int j=0; j < slices; ++j) {
int start_i = (i * noPerSlice) + j;
indicies.push_back( start_i );
indicies.push_back( start_i + noPerSlice + 1 );
indicies.push_back( start_i + noPerSlice );
indicies.push_back( start_i + noPerSlice + 1 );
indicies.push_back( start_i );
indicies.push_back( start_i + 1 );
}
}
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(4, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(glm::vec3), positions.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), normals.data(), GL_STATIC_DRAW);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_TRUE, 0, nullptr);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ARRAY_BUFFER, textureCoords.size() * sizeof(glm::vec2), textureCoords.data(), GL_STATIC_DRAW);
glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(8);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicies.size() * sizeof(GLuint), indicies.data(), GL_STATIC_DRAW);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
This is the rendering one:
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
if(style == glObject::STYLE::WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
if(style == glObject::STYLE::SOLID) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDrawElements(GL_TRIANGLES, (slices * stacks + slices) * 6, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
You have confused phi and theta. theta is the angle of the points around the circumference of a slice in range [0, 2*PI]. phi is the angle of the points form the south to the north in range [-PI, PI]:
for (int i = 0; i <= stacks; ++i) {
// V texture coordinate
float V = i / (float)stacks;
float phi = V * M_PI - M_PI/2.0;
for ( int j = 0; j <= slices; ++j) {
// U texture coordinate
float U = j / (float)slices;
float theta = U * _2pi;
float X = a * cos(phi) * cos(theta);
float Y = b * cos(phi) * sin(theta);
float Z = c * sin(phi);
positions.push_back( glm::vec3( X, Y, Z) );
normals.push_back( glm::vec3(X, Y, Z) );
textureCoords.push_back( glm::vec2(U, V) );
}
}
The number of points of a slice (around the circumference) is noPerSlice = slices + 1. The first index of a point of a quad is start_i = (i * noPerSlice) + j, where i is the index of the stack and j the index around the slice. Create slices quads around the circumference and stacks slices form the south to the north:
int noPerSlice = slices + 1;
for(int i=0; i < stacks; ++i) {
for (int j = 0; j < slices; ++j) {
int start_i = (i * noPerSlice) + j;
indicies.push_back( start_i );
indicies.push_back( start_i + noPerSlice + 1 );
indicies.push_back( start_i + noPerSlice );
indicies.push_back( start_i + noPerSlice + 1 );
indicies.push_back( start_i );
indicies.push_back( start_i + 1 );
}
}
i think
slices*stacks+slices
should be
slices*stacks+stacks
the +stacks is for the extra quads from the duplicate vertex in every stack
although this fixes the number of indices, you still have degenerate triangles along the duplicate vertices where theta equals zero
Here is an issue of my project, please look at the screenshots:
problems
original(correct)
My object is build in a wrong way. The vertices are not connected properly.
I suspect that it has something to do with the indices of the model. Anyway here is the code that constructs the mesh for me:
mesh model::processMesh(aiMesh * mesh_, const aiScene * scene)
{
std::vector<vertex> vertices;
std::vector<GLuint> indices;
std::vector<texture> textures;
//vertices
for (GLuint i = 0; i < mesh_->mNumVertices; i++)
{
vertex vert;
glm::vec3 vector;
//positions
vector.x = mesh_->mVertices[i].x;
vector.y = mesh_->mVertices[i].y;
vector.z = mesh_->mVertices[i].z;
vert.position = vector;
//normals
vector.x = mesh_->mNormals[i].x;
vector.y = mesh_->mNormals[i].y;
vector.z = mesh_->mNormals[i].z;
vert.normal = vector;
//texture coords
if (mesh_->mTextureCoords[0])
{
glm::vec2 vector_;
vector_.x = mesh_->mTextureCoords[0][i].x;
vector_.y = mesh_->mTextureCoords[0][i].y;
vert.texCoords = vector_;
}
else vert.texCoords = glm::vec2(0.0f, 0.0f);
vertices.push_back(vert);
}
//indices
for (GLuint i = 0; i < mesh_->mNumFaces; i++)
{
aiFace face = mesh_->mFaces[i];
for (GLuint j = 0; j < mesh_->mNumFaces; j++) indices.push_back(face.mIndices[j]);
}
//textures
if (mesh_->mMaterialIndex >= 0)
{
aiMaterial* material = scene->mMaterials[mesh_->mMaterialIndex];
//diffuse
std::vector<texture> diffuseMaps = this->loadMaterialTextures(material, aiTextureType_DIFFUSE, TEX_DIFF_NAME);
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
//specular
std::vector<texture> specularMaps = this->loadMaterialTextures(material, aiTextureType_SPECULAR, TEX_SPEC_NAME);
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
}
return mesh(vertices, indices, textures);
}
In this function I set up all objects:
void mesh::setupMesh()
{
//buffers
glGenVertexArrays(1, &this->vao);
glGenBuffers(1, &this->vbo);
glGenBuffers(1, &this->ebo);
glBindVertexArray(this->vao);
glBindBuffer(GL_ARRAY_BUFFER, this->vbo);
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);
//attributes
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, normal));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, texCoords));
//unbind
glBindVertexArray(0);
}
And here is the render
void mesh::draw(shader* shader)
{
GLuint tex_diffNumber = 1;
GLuint tex_specNumber = 1;
for (GLuint i = 0; i < this->textures.size() ; i++)
{
//load target texture
glActiveTexture(GL_TEXTURE0 + i);
std::stringstream sstream;
std::string number;
std::string name = this->textures[i].type;
if (name == TEX_DIFF_NAME)
sstream << tex_diffNumber++;
else if (name == TEX_SPEC_NAME)
sstream << tex_specNumber++;
number = sstream.str();
glBindTexture(GL_TEXTURE_2D, this->textures[i].id);
glUniform1i(glGetUniformLocation(shader->shaderProgID, (name + number).c_str()), i);
}
//set shininess
//glUniform1f(glGetUniformLocation(shader->shaderProgID, "material.shininess"), 16.0f);
//draw
glBindVertexArray(this->vao);
glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
//release
for (GLuint i = 0; i < this->textures.size(); i++)
{
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, 0);
}
}
The question is why does my model have wrong built triangles?
Ask any additional info.
Thank you for help in advance, guys!
for (GLuint i = 0; i < mesh_->mNumFaces; i++)
{
aiFace face = mesh_->mFaces[i];
for (GLuint j = 0; j < mesh_->mNumFaces; j++)
indices.push_back(face.mIndices[j]);
}
This looks wrong to me. I don't think the inner loop should run over all faces again. Instead it should iterate over all indices in the face:
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]);
}
Note, that the original version was reading outside of the available memory, since the loop was most probably going till j = 5 (six faces), but there are only faces with a maximum of four indices. If one would have attached a debugger and stepped through it, this should have been easily visible.
Hi everyone i'm making a little 3d engine for my games and i created a class for vertex arrays and VBO. It was working until i tried to add indexing to the vertex array. Now it only draws the first element indexed!
Here is the function build, that generates the VAO and the VBOs.
void build() {
vboID = new GLuint[vboS];
if (vertexIndexing) {
for (int n = 0, m = 0, i = 0; n < vertexes.size(); m++) {
if (i + m >= vertexes.size()) {
break;
}
indices.push_back((GLushort)(i+m));
if (m >= s) {
m = 0;
n++;
i+=s/2;
std::cout << "\n";
}
std::cout << i + m << " , ";
}
std::cout << "\n";
}
shader->use();
glGenVertexArrays(1, &vaoID);
glBindVertexArray(vaoID);
glGenBuffers(vboS, vboID);
if (vertexIndexing) {
glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLushort), &indices[0], GL_STATIC_DRAW);
}
int n = 0;
if (options.vertex) {
shader->addAttribute("vertex");
glBindBuffer(GL_ARRAY_BUFFER, vboID[n]);
glBufferData(GL_ARRAY_BUFFER, pos.size() * sizeof(GLfloat), &pos[0], GL_STATIC_DRAW);
glVertexAttribPointer(shader->attribute("vertex"), 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(shader->attribute("vertex"));
n++;
}
if (options.color) {
shader->addAttribute("color");
glBindBuffer(GL_ARRAY_BUFFER, vboID[n]);
glBufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(GLfloat), &colors[0], GL_STATIC_DRAW);
glVertexAttribPointer(shader->attribute("color"), 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(shader->attribute("color"));
n++;
}
if (options.normal) {
shader->addAttribute("normal");
glBindBuffer(GL_ARRAY_BUFFER, vboID[n]);
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(GLfloat), &normals[0], GL_STATIC_DRAW);
glVertexAttribPointer(shader->attribute("normal"), 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(shader->attribute("normal"));
n++;
}
if (options.texcoord) {
shader->addAttribute("texcoord");
glBindBuffer(GL_ARRAY_BUFFER, vboID[n]);
glBufferData(GL_ARRAY_BUFFER, texcoords.size() * sizeof(GLfloat), &texcoords[0], GL_STATIC_DRAW);
glVertexAttribPointer(shader->attribute("texcoord"), 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(shader->attribute("texcoord"));
n++;
}
glBindVertexArray(0);
shader->disable();
}
Here is the function draw, that draws the VAO.
void draw() {
glBindVertexArray(vaoID);
if (vertexIndexing) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
shader->use();
glDrawElements(mode, indices.size(), GL_UNSIGNED_SHORT, 0);
shader->disable();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
else {
shader->use();
glDrawArrays(mode, 0, vertexes.size());
shader->disable();
}
glBindVertexArray(0);
}
Note: I removed glEnableClientState(GL_VERTEX_ARRAY) because it isnt doing anything.
Ok, I fixed it. It wasn't openGL's problem.
It was with the indices generation.
NEW INDICES GENERATION:
for (int n = 0, m = 0, i = 0; n < vertexes.size(); m++) {
if (i + m >= vertexes.size()) {
break;
}
if (m >= s) {
m = 0;
n++;
i+=s/2;
}
indices.push_back((GLushort)(i + m));
}