I've been working on my Model Loader and I got an error because I use several structs with arrays (no vectors), I want to initialize the array before use it because the program gives me error when I try to use to access the vector array with brackets [].
GameObject struct:
struct GameObject
{
int ID, parent;
string tag;
Mesh_t Mesh;
Vector3 position;
Vector3 scale;
Quaternion rotation;
Color color;
};
Meshes struct:
struct Meshes_t
{
vector<GLfloat> VBO;
vector<GLfloat> VBO_Normal;
Texture_t Texture;
DWORD Geometry;
int VertexType;
};
struct Mesh_t
{
int VerticesCount;
int TexturesCount;
vector<Meshes_t> Faces;
};
And the problem is when I try to push_back to VBO vector array from "Meshes_t" like this:
GameObjects[GameObjectsCount].Mesh.Faces[k].VBO.push_back(vertex.x);
GameObjects[GameObjectsCount].Mesh.Faces[k].VBO.push_back(vertex.y);
GameObjects[GameObjectsCount].Mesh.Faces[k].VBO.push_back(vertex.z);
Due to Faces vector array is not initialized anywhere Visual Studio throws me a runtime error when the program reach the code below: this is the error https://gyazo.com/806e3426025ad3885f0d3ed0b7aa1d30
I tried to initialize the vector array but I don't know how to do it because the vector array has other struct inside and I can't push back struct data. Any ideas? Thanks in advance.
EDIT:
This is the gameobjects array from .h
extern struct GameObject GameObjects[1000];
and cpp is struct GameObject GameObjects[1000];
so I use this to create gameobject:
int teste = Entity.CreateEntity("data//models//cube.obj", Vector3(0, 25, 0), Quaternion(0, 0, 0), Vector3(0.51, 0.51, 0.51), -1);
and the int returned is what I use in GameObjects[ReturnedINT]
RENDER FUNCTION:
void GLRender ()
{
glBindTexture(GL_TEXTURE_2D, 0);
RenderSpaceLines();
drawLine(Vector3(0, 0, 0), GameObjects[0].position);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
for (int i = 0; i < Entity.GameObjectsCount; i++)
{
glBindTexture(GL_TEXTURE_2D, 0);
if (GameObjects[i].Mesh.TexturesCount > 0)
{
for (int t = 0; t < 10; t++)
{
glVertexPointer(3, GL_FLOAT, 0, (GLfloat*)GameObjects[i].Mesh.Faces[t].VBO.data());
glTexCoordPointer(2, GL_FLOAT, 0, (GLfloat*)GameObjects[i].Mesh.Faces[t].Texture.VBO.data());
glBindTexture(GL_TEXTURE_2D, TextureManager.GetTextureFromName(GameObjects[i].Mesh.Faces[t].Texture.TextureName));
glPushMatrix();
glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (GameObjects[i].parent == -1)
{
glTranslatef(GameObjects[i].position.x, GameObjects[i].position.y, GameObjects[i].position.z);
glRotatef(GameObjects[i].rotation.x, 1, 0, 0);
glRotatef(GameObjects[i].rotation.y, 0, 1, 0);
glRotatef(GameObjects[i].rotation.z, 0, 0, 1);
}
else ///RELATIVITY OF ENTITY TO ANOTHER
{
glTranslatef(GameObjects[GameObjects[i].parent].position.x,
GameObjects[GameObjects[i].parent].position.y,
GameObjects[GameObjects[i].parent].position.z); ///SET WORLD RELATIVE POSITION
glRotatef(GameObjects[GameObjects[i].parent].rotation.x, 1, 0, 0);
glRotatef(GameObjects[GameObjects[i].parent].rotation.y, 0, 1, 0);
glRotatef(GameObjects[GameObjects[i].parent].rotation.z, 0, 0, 1);
glTranslatef(GameObjects[i].position.x,
GameObjects[i].position.y,
GameObjects[i].position.z);
}
glScalef(GameObjects[i].scale.x,
GameObjects[i].scale.y,
GameObjects[i].scale.z);
glDrawArrays(GameObjects[i].Mesh.Faces[t].Geometry, 0, GameObjects[i].Mesh.Faces[t].VertexType);
glPopMatrix();
}
}
}
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
Faces is actually initialized in the implicit default constructor of Mesh_t. But it will be empty. So, before accessing Faces[k] you must first put at least k + 1 elements into the vector. For example:
GameObjects[GameObjectsCount].Mesh.Faces.resize(k + 1);
GameObjects[GameObjectsCount].Mesh.Faces[k].VBO.push_back(vertex.x);
// ...
Also make sure that GameObjects has at least GameObjectsCount + 1 objects.
Related
I'm brand new to OpenGL and am having some difficulty rendering multiple objects.
I have a vector each of which has its own VertexBuffer. Then, in the while loop I draw each shape on its own.
It's all well and good when I have many of the same objects (multiple cubes etc.) however, when I add a triangle mesh everything gets all out of whack.
I can have many cubes
I can have a single triangle mesh:
But, when I try to have a cube and then a triangle mesh I get:
I'm totally at a loss for what's going on. The code for my loop is provided below.
while (!glfwWindowShouldClose(window))
{
// Get the size of the window
int width, height;
glfwGetWindowSize(window, &width, &height);
float aspect_ratio = 1 * float(height)/float(width); // corresponds to the necessary width scaling
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
// Clear the framebuffer
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Enable depth test
glEnable(GL_DEPTH_TEST);
glUniform3f(program.uniform("triangleColor"), 1.0f, 1.0f, 1.0f);
glUniformMatrix4fv(program.uniform("proj"), 1, GL_FALSE, projection.data());
glUniformMatrix4fv(program.uniform("view"), 1, GL_FALSE, view.data());
int tally = 0;
for (int i = 0; i < surfaces.size(); i++) {
Surface *s = surfaces[i];
Vector3f color = s->getColor();
int tempIndex = triangleIndex;
Matrix4f model = s->getModel();
// Convert screen position to world coordinates
double xworld = ((xpos/double(width))*2)-1;
double yworld = (((height-1-ypos)/double(height))*2)-1; // NOTE: y axis is flipped in glfw
if (isPressed && mode == "translate") {
if(tempIndex == i) {
Vector4f center = s->getCenter() + model.col(3);
Vector4f displacement = Vector4f(xworld, yworld, 0, 1) - center;
Matrix4f translation = translateMatrix(displacement(0), displacement(1), displacement(2));
model = translation * s->getModel();
s->setModel(model);
}
}
glUniform3f(program.uniform("triangleColor"), color(0), color(1), color(2));
glUniformMatrix4fv(program.uniform("model"), 1, GL_FALSE, model.data());
glDrawArrays(GL_TRIANGLES, 0, s->getVertices().size());
}
And I initialize each VBO when making the object as
VertexBufferObject VBO;
VBO.init();
VBO.update(Vertices);
program.bindVertexAttribArray("position", VBO);
Surface* s = new Surface(VBO, Vertices, percentScale, 0, transformedCenter, SmoothNormals, FlatNormals, color);
s->setModel(model);
surfaces.push_back(s);
And where Program::bindVertexAttribArray is defined as
GLint Program::bindVertexAttribArray(
const std::string &name, VertexBufferObject& VBO) const
{
GLint id = attrib(name);
if (id < 0)
return id;
if (VBO.id == 0)
{
glDisableVertexAttribArray(id);
return id;
}
VBO.bind();
glEnableVertexAttribArray(id);
glVertexAttribPointer(id, VBO.rows, GL_FLOAT, GL_FALSE, 0, 0);
check_gl_error();
return id;
}
You're not binding any buffers before the draw call. You're probably simply drawing whatever buffer you last bound when you initialised them. You'll need something like this at the end of your loop before glDrawArrays:
...
program.bindVertexAttribArray("position", VBO); // where VBO is the buffer of surface s
glUniform3f(program.uniform("triangleColor"), color(0), color(1), color(2));
glUniformMatrix4fv(program.uniform("model"), 1, GL_FALSE, model.data());
glDrawArrays(GL_TRIANGLES, 0, s->getVertices().size());
This question already has an answer here:
Mesh class called with default constructor not working OpenGL C++
(1 answer)
Closed 6 years ago.
I'm running through a weird problem. Basically I have Mesh class depending on a flag, I can draw a point, a line, or a triangle. For example, if I want to draw two lines, I can do the following
Vertex vertices1[] = {
Vertex(glm::vec3(-.5, -.5, 0)),
Vertex(glm::vec3( 0, .5, 0))
};
Vertex vertices2[] = {
Vertex(glm::vec3( .5, -.5, 0)),
Vertex(glm::vec3( -.5, .5, 0))
};
Mesh mesh1(vertices1, sizeof(vertices1)/sizeof(vertices1[0]), 'L');
Mesh mesh2(vertices2, sizeof(vertices2)/sizeof(vertices2[0]), 'L');
// Rendering Loop:
while( Window.isOpen() ){
...
//================( Rendering )=========================
ourShader.Use();
mesh1.draw();
mesh2.draw();
//======================================================
...
}
The result is
Now I would like to use std::vector<Mesh> and loop through meshes. My attempt is as follows
std::vector<Mesh> meshes;
meshes.push_back(mesh1);
meshes.push_back(mesh2);
while( Window.isOpen() ){
...
//================( Rendering )=========================
ourShader.Use();
for ( int i(0); i < meshes.size(); ++i )
meshes[i].draw();
//======================================================
...
}
With the preceding approach, only the last line is drawn and this is the result
Moreover, once I use .push_back() even if I don't loop through the vector, the last line is drawn. I don't understand why using std::vector deteriorates the rendering. I even tried meshes[0].draw() but with no luck. Any suggestions?
Edit:
This is the constructor of Mesh class
#include <iostream>
#include <vector>
#include <glm/glm.hpp>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "display.h"
#include "keyboard.h"
#include "shader.h"
class Vertex
{
public:
Vertex(const glm::vec3& p) : m_position(p)
{}
private:
glm::vec3 m_position;
};
class Mesh
{
public:
Mesh(Vertex* vertices, unsigned int numVertices, const char& flag);
~Mesh();
void draw();
private:
enum{
POSITION_VB,
NUM_BUFFERS
};
GLuint m_vertexArrayObject;
GLuint m_vertexArrayBuffers[NUM_BUFFERS];
unsigned int m_drawCount;
char m_flag;
};
Mesh::Mesh(Vertex* vertices, unsigned int numVertices, const char& flag) : m_flag(flag), m_drawCount(numVertices)
{
glGenVertexArrays(1, &m_vertexArrayObject);
glBindVertexArray(m_vertexArrayObject);
glGenBuffers(NUM_BUFFERS, m_vertexArrayBuffers);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[POSITION_VB]);
glBufferData(GL_ARRAY_BUFFER, numVertices*sizeof(vertices[0]), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindVertexArray(0);
}
Mesh::~Mesh()
{
glDeleteVertexArrays(1, &m_vertexArrayObject);
glDeleteBuffers(1, m_vertexArrayBuffers);
}
void Mesh::draw()
{
switch(m_flag)
{
case 'P':
glBindVertexArray(m_vertexArrayObject);
glDrawArrays(GL_POINTS, 0, m_drawCount);
glBindVertexArray(0);
break;
case 'L':
glBindVertexArray(m_vertexArrayObject);
glDrawArrays(GL_LINES, 0, m_drawCount);
glBindVertexArray(0);
break;
case 'T':
glBindVertexArray(m_vertexArrayObject);
glDrawArrays(GL_TRIANGLES, 0, m_drawCount);
glBindVertexArray(0);
break;
}
}
int main(void)
{
Display Window(800, 600, "OpenGL Window");
Keyboard myKeyboard( Window.getWindowPointer() );
Vertex vertices1[] = {
Vertex(glm::vec3(-.5, -.5, 0)),
Vertex(glm::vec3( 0, .5, 0))
};
Vertex vertices2[] = {
Vertex(glm::vec3( .5, -.5, 0)),
Vertex(glm::vec3( -.5, .5, 0))
};
Mesh mesh1(vertices1, sizeof(vertices1)/sizeof(vertices1[0]), 'L');
Mesh mesh2(vertices2, sizeof(vertices2)/sizeof(vertices2[0]), 'L');
std::vector<Mesh> meshes;
meshes.emplace_back(mesh1);
meshes.emplace_back(mesh2);
std::cout << meshes.size() << std::endl;
//*****************( SHADER )************************
Shader ourShader("shader.vs", "shader.frag");
glEnable(GL_PROGRAM_POINT_SIZE);
while( Window.isOpen() ){
Window.PollEvents();
Window.clear();
//================( Rendering )=========================
ourShader.Use();
//mesh1.draw();
//mesh2.draw();
for ( int i(0); i < meshes.size(); ++i )
meshes[i].draw();
//meshes[0].draw();
//meshes[1].draw();
//======================================================
Window.SwapBuffers();
}
glfwTerminate();
return 0;
}
Shaders
#version 330 core
out vec4 color;
void main()
{
color = vec4(1.0f,0.5f,0.2f,1.0f);
}
#version 330 core
layout (location = 0) in vec3 position;
void main()
{
gl_PointSize = 10.0;
gl_Position = vec4(position, 1.0);
}
As I suspected, the problem is with the (lack of) copy constructor. The default one just copies all the members. As a result your VAOs and buffers get deleted multiple times, even before you manage to draw anything (vectors move during reallocation, and if they can't move they copy). As a rule of thumb: if you have a non-default destructor, you must implement also a copy constructor and an assignment operator, or explicitly delete them if your class is not meant to be copyable.
For your concrete case the solutions are:
Quick solution: store pointers to meshes in the vector:
std::vector<Mesh*> meshes;
meshes.emplace_back(&mesh1);
meshes.emplace_back(&mesh2);
Correct solution: use proper RAII for resource management. Using the unique_ptr technique from here your MCVE code becomes:
class Mesh
{
public:
Mesh(Vertex* vertices, unsigned int numVertices, const char& flag);
void draw();
private:
//...
GLvertexarray m_vertexArrayObject;
GLbuffer m_vertexArrayBuffers[NUM_BUFFERS];
unsigned int m_drawCount;
char m_flag;
};
Mesh::Mesh(Vertex* vertices, unsigned int numVertices, const char& flag) : m_flag(flag), m_drawCount(numVertices)
{
GLuint id;
glGenVertexArrays(1, &id);
glBindVertexArray(id);
m_vertexArrayObject.reset(id);
for(int i = 0; i < NUM_BUFFERS; ++i)
{
glGenBuffers(1, &id);
glBindBuffer(GL_ARRAY_BUFFER, id);
m_vertexArrayBuffers[i].reset(id);
glBufferData(GL_ARRAY_BUFFER, numVertices*sizeof(vertices[0]), vertices, GL_STATIC_DRAW);
}
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindVertexArray(0);
}
void Mesh::draw()
{
switch(m_flag)
{
case 'P':
glBindVertexArray(m_vertexArrayObject.get());
glDrawArrays(GL_POINTS, 0, m_drawCount);
glBindVertexArray(0);
break;
case 'L':
glBindVertexArray(m_vertexArrayObject.get());
glDrawArrays(GL_LINES, 0, m_drawCount);
glBindVertexArray(0);
break;
case 'T':
glBindVertexArray(m_vertexArrayObject.get());
glDrawArrays(GL_TRIANGLES, 0, m_drawCount);
glBindVertexArray(0);
break;
}
}
int main()
{
//...
Mesh mesh1(vertices1, sizeof(vertices1)/sizeof(vertices1[0]), 'L');
Mesh mesh2(vertices2, sizeof(vertices2)/sizeof(vertices2[0]), 'L');
std::vector<Mesh> meshes;
meshes.emplace_back(std::move(mesh1));
meshes.emplace_back(std::move(mesh2));
// ...
return 0;
}
Notice how there is no more need for defining a destructor, and your class automatically becomes movable but not copyable. Furthermore, if you have OpenGL 4.5 or ARB_direct_state_access then things get even simpler.
EDIT
The main problem is, that the destructor is called when you add the Mesh objects to the vector, therefore the underlying data gets cleaned up.
Further reading: Why does my class's destructor get called when I add instances to a vector? | What is The Rule of Three?
I'd personally create separate init_buffers and free_buffers methods to my Mesh class and use them appropriately. (Initialize buffers after the OpenGL context is obtained, free the buffers when the window is closed.)
This way you can start building meshes (and add them to the scene) before actually having the OpenGL context.
I've implemented the missing code parts and tried your code using GLFW using CLion.
It works. See code / CLion project here: OpenGLSandbox/main.cpp
The only code I've added are basically these, so it's your turn to figure out the difference / error.
// Constants
const size_t NUM_BUFFERS = 1;
const size_t POSITION_VB = 0;
// Vertex class
class Vertex {
private:
glm::vec3 mCoords;
public:
Vertex(glm::vec3 coords) : mCoords(coords) {};
};
// Mesh class
class Mesh {
private:
GLuint m_vertexArrayObject;
char m_flag;
unsigned int m_drawCount;
GLuint m_vertexArrayBuffers[NUM_BUFFERS];
public:
/* your ctor and draw method */
}
Currently I am trying to draw a triangle via a mesh class. I do this by first initializing the glew and the calling the Window.initializeGraphics method of my Window class, then creatubg an array of verticies and passing to my Mesh.addVerticies method. Each vertex has 3 floats, x, y, and z. Then I call the Mesh.draw method every tick of the main game loop.
initializeGraphics Method:
void Window::initializeGraphics()
{
glClearColor(0, 0, 0, 0);
glFrontFace(GL_CW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_FRAMEBUFFER_SRGB);
}
Creating the Mesh:
m_Mesh = Mesh();
Vertex data[] = { Vertex(vec3(-1, -1, 0)),
Vertex(vec3(1, -1, 0)),
Vertex(vec3(0, 1, 0)) };
m_Mesh.addVerticies(data);
Mesh Header:
#include "vertex.h"
#include <GLEW\glew>
class Mesh
{
private:
GLuint m_Vbo;
int m_Size;
public:
Mesh();
void addVerticies(Vertex verticies[]);
void draw();
};
Mesh C++ File:
#include "mesh.h"
Mesh::Mesh()
{
glGenBuffers(1, &m_Vbo);
}
void Mesh::addVerticies(Vertex verticies[])
{
m_Size = (sizeof(verticies) / sizeof(*verticies));
glBindBuffer(GL_ARRAY_BUFFER, m_Vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(verticies), verticies, GL_STATIC_DRAW);
}
void Mesh::draw()
{
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, m_Vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * 4, 0);
glDrawArrays(GL_TRIANGLES, 0, m_Size);
glDisableVertexAttribArray(0);
}
Vertex Header:
#include "vec3.h"
struct Vertex
{
union
{
vec3 pos;
struct
{
float x, y, z;
};
};
Vertex(vec3 pos_);
};
Vertex C++ File:
#Include "vertex.h"
Vertex::Vertex(vec3 pos_)
{
pos = pos_;
}
Render Method:
void MainComponent::render()
{
m_Window.clear();
m_Mesh.draw();
m_Window.update();
}
//m_Window.update();
void Window::update()
{
glfwSwapBuffers(m_GLFWWindow);
glfwPollEvents();
}
//m_Window.clear();
void Window::clear()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
Problem is, no triangle appears on screen. What am I doing wrong ? I am pretty much still a C++ newling, and this is my first time programming OpenGl so it could be something really really basic that I keep overlooking.
Thanks in advance for your efforts.
-Sean
Woah, hold on there! There are other bona fide errors here.
void Mesh::addVerticies(Vertex verticies[])
{
// This gives the wrong answer!
m_Size = (sizeof(verticies) / sizeof(*verticies));
That calculation for m_Size is completely wrong. This is because verticies is a pointer not an array. It looks like an array, it was declared as Vertex verticies[], but due to a quirk of C++ (the same applies to C) the parameter decays into a pointer. So it ends up being the same as this:
void Mesh::addVerticies(Vertex *verticies)
As you can see, sizeof(verticies) is always going to be 8 on a typical 64-bit system, because you are just getting the size of a pointer, not the size of an array.
We can fix this by capturing the size of the array with a template:
template <std::size_t N>
void addVertices(Vertex (&vertices)[N]) {
addVertices(vertices, N);
}
void addVertices(Vertex *vertices, std::size_t count) {
m_Size = count;
glBindBuffer(GL_ARRAY_BUFFER, m_Vbo);
glBufferData(
GL_ARRAY_BUFFER,
sizeof(*vertices) * count,
vertices,
GL_STATIC_DRAW);
}
Because we use a reference to the array, Vertex (&vertices)[N], instead of the array directly, Vertex vertices[N], the parameter does not decay into a pointer.
I feel like a complete moron... One verticie was off the screen. I think I'll call it a day :/
I am getting frustrated about this. I am trying to render a cube with Vertex Buffer Objects and I was learning about Projection so I am trying to make a frame of a cube. However, this code does not work. When I run the program on Code::Blocks, the program stops working.
I tried to find out the reason by commenting out the content of the render() method, and then the program doesn't stop working. So this line of code
glDrawElements(GL_TRIANGLES, m_index->size(), GL_UNSIGNED_INT, 0); // render the cube
may be the source of the problem. But I don't know how to fix this, because this is just what I usually do (I have made similar programs and they worked)
I really really appreciate your help!
ProjectionCube::ProjectionCube()
{
rotationAngle = 0.0;
}
bool ProjectionCube::initialize()
{
#ifdef _WIN32
glGenBuffers = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffers");
glBindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");
glBufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData");
#else
glGenBuffers = (PFNGLGENBUFFERSARBPROC)glXGetProcAddress((const GLubyte*)"glGenBuffers");
glBindBuffer = (PFNGLBINDBUFFERPROC)glXGetProcAddress((const GLubyte*)"glBindBuffer");
glBufferData = (PFNGLBUFFERDATAPROC)glXGetProcAddress((const GLubyte*)"glBufferData");
#endif
if (!glGenBuffers || !glBindBuffer || !glBufferData)
{
std::cerr << "VBOs are not supported by your graphics card" << std::endl;
return false;
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
initializeVertexBuffers();
// bind vertex buffer and index buffer
// set vertex pointer to the buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vbos[INDEX_BUFFER]);
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[VERTEX_BUFFER]);
glVertexPointer(3, GL_FLOAT, 0, 0);
// no color buffer to bind
return true;
}
void ProjectionCube::initializeVertexBuffers()
{
const float size = 0.5f;
m_vertex = getCubeVertices(size);
for (int i = 0; i < m_vertex->size(); i++) {
std::cout << m_vertex->at(i) << ", ";
}
std::cout << std::endl;
static const unsigned int index[] = {//using triangles to render
0, 1, 2, 0, 2, 3, //bottom
0, 4, 5, 0, 1, 5, //back
0, 4, 7, 0, 3, 7, //left
1, 5, 6, 1, 2, 6, //right
4, 5, 6, 4, 7, 6, //top
2, 6, 7, 2, 3, 7}; // front
m_index = new vector<unsigned int>(index, index + sizeof(index) / sizeof(index[0]));
for (int i = 0; i < m_index->size(); i++) {
std::cout << m_index->at(i) << ", ";
}
std::cout << std::endl;
glGenBuffers(1, &m_vbos[VERTEX_BUFFER]);
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[VERTEX_BUFFER]);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * m_vertex->size(),
&m_vertex->at(0), GL_STATIC_DRAW);
glGenBuffers(1, &m_vbos[INDEX_BUFFER]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vbos[INDEX_BUFFER]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * m_index->size(),
&m_index->at(0), GL_STATIC_DRAW);
}
void ProjectionCube::render(float m_x, float m_y, float m_z)
{
glTranslatef(m_x, m_y, m_z); // move to where the cube is located
//glRotatef(rotationAngle, 0.5f, 0.0f, 0.0f);
glDrawElements(GL_TRIANGLES, m_index->size(), GL_UNSIGNED_INT, 0); // render the cube
}
void ProjectionCube::animate(float dt)
{
const float SPEED = 15.0f;
rotationAngle += SPEED * dt;
if (rotationAngle >= 360 || rotationAngle <= 0) {
rotationAngle = -rotationAngle;
}
}
vector<GLfloat>* ProjectionCube::getCubeVertices(float r)
{
static const GLfloat vertices[] = {//bottom square
-r, -r, -r,
r, -r, -r,
r, -r, r,
-r, -r, r,
//top square
-r, r, -r,
r, r, -r,
r, r, r,
-r, r, r,};
vector<GLfloat>* result = new vector<GLfloat>(vertices, vertices + sizeof(vertices) / sizeof(vertices[0]));
return result;
}
Since you didn't post when render is called, all I can do is advise you into not using new vector. There is no need for it as far as I can see.
Since your error occurs when you use m_index in the render function, and assuming m_index is a pointer to a vector, then there is no need for it to be a pointer (assuming it's a member variable of ProjectionCube).
There are two issues with new vector. Why does your program dynamically allocate a in the getCubeVertices function? The following removes the dynamic allocation:
vector<GLfloat> ProjectionCube::getCubeVertices(float r)
{
static const GLfloat vertices[] = {//bottom square
-r, -r, -r,
r, -r, -r,
r, -r, r,
-r, -r, r,
//top square
-r, r, -r,
r, r, -r,
r, r, r,
-r, r, r,};
return vector<GLfloat>(vertices, vertices + sizeof(vertices) / sizeof(vertices[0]));
}
Then in InitializeVertexBuffers(), the m_vertex member variable is no longer a pointer, but an object:
std::vector<GLfloat> m_vertex; // assuming these are member variables in your class
std::vector<unsigned int> m_index;
//...
void ProjectionCube::initializeVertexBuffers()
{
const float size = 0.5f;
m_vertex = getCubeVertices(size);
//...
m_index = vector<unsigned int>(index, index + sizeof(index) / sizeof(index[0]));
Again, no need for new vector. You now use m_index. and m_vertex..
Now what does this buy you, as opposed to what you were doing before? Well, now you're guaranteed that m_index is valid when render is called. There is no way for m_index to have been deallocated, or the pointer corrupted, etc. since m_index is no longer a pointer.
You also rid yourself of a potential memory leak using the above approach.
I have a simple OpenGL program which I am trying to utilize Vertex Buffer Objects for rendering instead of the old glBegin() - glEnd(). Basically the user clicks on the window indicating a starting point, and then presses a key to generate subsequent points which OpenGL draws as a line.
I've implemented this using glBegin() and glEnd() but have not been successful using a VBO. I am wondering if the problem is that after I initialize the VBO, I'm adding more vertices which it doesn't have memory allocated for, and thus doesn't display them.
Edit: Also, I'm a bit confused as to how it knows exactly which values in the vertex struct to use for x and y, as well as for r, g, b. I haven't been able to find a clear example of this.
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <Math.h>
#include <iostream>
#include <vector>
#include <GL/glew.h>
#include <GL/glut.h>
struct vertex {
float x, y, u, v, r, g, b;
};
const int D = 10; // distance
const int A = 10; // angle
const int WINDOW_WIDTH = 500, WINDOW_HEIGHT = 500;
std::vector<vertex> vertices;
boolean start = false;
GLuint vboId;
void update_line_point() {
vertex temp;
temp.x = vertices.back().x + D * vertices.back().u;
temp.y = vertices.back().y + D * vertices.back().v;
temp.u = vertices.back().u;
temp.v = vertices.back().v;
vertices.push_back(temp);
}
void update_line_angle() {
float u_prime, v_prime;
u_prime = vertices.back().u * cos(A) - vertices.back().v * sin(A);
v_prime = vertices.back().u * sin(A) + vertices.back().v * cos(A);
vertices.back().u = u_prime;
vertices.back().v = v_prime;
}
void initVertexBuffer() {
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex) * vertices.size(), &vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void displayCB() {
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, WINDOW_WIDTH, 0, WINDOW_HEIGHT);
if (start) {
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(vertex), &vertices[0]);
glColorPointer(3, GL_FLOAT, sizeof(vertex), &vertices[0]);
glDrawArrays(GL_LINE_STRIP, 0, vertices.size());
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
/***** this is what I'm trying to achieve
glColor3f(1, 0, 0);
glBegin(GL_LINE_STRIP);
for (std::vector<vertex>::size_type i = 0; i < vertices.size(); i++) {
glVertex2f(vertices[i].x, vertices[i].y);
}
glEnd();
*****/
glFlush();
glutSwapBuffers();
}
void mouseCB(int button, int state, int x, int y) {
if (state == GLUT_DOWN) {
vertices.clear();
vertex temp = {x, WINDOW_HEIGHT - y, 1, 0, 1, 0, 0}; // default red color
vertices.push_back(temp);
start = true;
initVertexBuffer();
}
glutPostRedisplay();
}
void keyboardCB(unsigned char key, int x, int y) {
switch(key) {
case 'f':
if (start) {
update_line_point();
}
break;
case 't':
if (start) {
update_line_angle();
}
break;
}
glutPostRedisplay();
}
void initCallbackFunc() {
glutDisplayFunc(displayCB);
glutMouseFunc(mouseCB);
glutKeyboardFunc(keyboardCB);
}
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH);
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(100, 100);
glutCreateWindow("Test");
initCallbackFunc();
// initialize glew
GLenum glewInitResult;
glewExperimental = GL_TRUE;
glewInitResult = glewInit();
if (GLEW_OK != glewInitResult) {
std::cerr << "Error initializing glew." << std::endl;
return 1;
}
glClearColor(1, 1, 1, 0);
glutMainLoop();
return 0;
}
If you have a VBO bound then the pointer argument to the gl*Pointer() calls is interpreted as a byte offset from the beginning of the VBO, not an actual pointer. Your usage is consistent with vertex array usage though.
So for your vertex struct x starts at byte zero and r starts at byte sizeof(float) * 4.
Also, your mouse callback reset your vertex vector on every call so you would never be able have more than one vertex in it at any given time. It also leaked VBO names via the glGenBuffers() in initVertexBuffer().
Give this a shot:
#include <GL/glew.h>
#include <GL/glut.h>
#include <iostream>
#include <vector>
struct vertex
{
float x, y;
float u, v;
float r, g, b;
};
GLuint vboId;
std::vector<vertex> vertices;
void mouseCB(int button, int state, int x, int y)
{
y = glutGet( GLUT_WINDOW_HEIGHT ) - y;
if (state == GLUT_DOWN)
{
vertex temp = {x, y, 1, 0, 1, 0, 0}; // default red color
vertices.push_back(temp);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex) * vertices.size(), &vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glutPostRedisplay();
}
void displayCB()
{
glClearColor(1, 1, 1, 0);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
glOrtho( 0, w, 0, h, -1, 1 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
if ( vertices.size() > 1 )
{
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(vertex), (void*)(sizeof( float ) * 0));
glColorPointer(3, GL_FLOAT, sizeof(vertex), (void*)(sizeof( float ) * 4));
glDrawArrays(GL_LINE_STRIP, 0, vertices.size());
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glutSwapBuffers();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow("Test");
// initialize glew
glewExperimental = GL_TRUE;
GLenum glewInitResult = glewInit();
if (GLEW_OK != glewInitResult) {
std::cerr << "Error initializing glew." << std::endl;
return 1;
}
glGenBuffers(1, &vboId);
glutDisplayFunc(displayCB);
glutMouseFunc(mouseCB);
glutMainLoop();
return 0;
}
A VBO is a buffer located somewhere in memory (almost always in dedicated GPU memory - VRAM) of a fixed size. You specify this size in glBufferData, and you also simultaneously give the GL a pointer to copy from. The key word here is copy. Everything you do to the vector after glBufferData isn't reflected in the VBO.
You should be binding and doing another glBufferData call after changing the vector. You will also probably get better performance from glBufferSubData or glMapBuffer if the VBO is already large enough to handle the new data, but in a small application like this the performance hit of calling glBufferData every time is basically non-existent.
Also, to address your other question about the values you need to pick out x, y, etc. The way your VBO is set up is that the values are interleaved. so in memory, your vertices will look like this:
+-------------------------------------------------
| x | y | u | v | r | g | b | x | y | u | v | ...
+-------------------------------------------------
You tell OpenGL where your vertices and colors are with the glVertexPointer and glColorPointer functions respectively.
The size parameter specifies how many elements there are for each vertex. In this case, it's 2 for vertices, and 3 for colors.
The type parameter specifies what type each element is. In your case it's GL_FLOAT for both.
The stride parameter is how many bytes you need to skip from the start of one vertex to the start of the next. With an interleaved setup like yours, this is simply sizeof(vertex) for both.
The last parameter, pointer, isn't actually a pointer to your vector in this case. When a VBO is bound, pointer becomes a byte offset into the VBO. For vertices, this should be 0, since the first vertex starts at the very first byte of the VBO. For colors, this should be 4 * sizeof(float), since the first color is preceded by 4 floats.