Related
Runs only once
GLfloat vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
-0.5f, 0.5f, 0.0f
};
GLuint triangles[] = {0,1,2};
int lengthV = 3; //Number of vertices
int lenfthT = 1; //Number of triangles
glGenVertexArrays(1, &vaoID);
glBindVertexArray(vaoID);
glGenBuffers(vaoID, &verticesVBOID);
glBindBuffer(GL_ARRAY_BUFFER, verticesVBOID);
glBufferData(GL_ARRAY_BUFFER, lengthV*sizeof(GLfloat)*3, vertices, GL_STATIC_DRAW);
glGenBuffers(vaoID, &trianglesVBOID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, trianglesVBOID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, lengthT*sizeof(GLuint)*3, triangles, GL_STATIC_DRAW);
Runs once per frame
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, verticesVBOID);
glVertexAttribPointer(0, lengthV, GL_FLOAT, GL_FALSE, 0, nullptr);
glUseProgram(program);
//glDrawArrays(GL_TRIANGLES, 0, verticeCount*3);
glDrawElements(GL_TRIANGLES, lengthT,GL_UNSIGNED_INT, nullptr);
glDisableVertexAttribArray(0);
glfwSwapBuffers(window);
When I run this, this renders, which is right.
When I change the number of vertices to 4 to try and render a quad eventually.
int lengthV = 4;
This happens.
I don't understand why this is happening because I only changed the number of vertices.
Ive Tried to find an answer but I could not. I have looked at this persons problem and his code looks the same as mine after applying the answer.
No display from glDrawElements
You missinterpret the second parameter of glVertexAttribPointer.
The size parameter specifies the number of components of an attribute (which basically is the number of floats it gets). Since you still have a vec3 (3 floats) per vertex when drawing a quad, this number shouldn't be changed.
I am following along with a youtube tutorial concerning opengl (a graphics programming library). I've created classes called vertex, ShapeData and ShapeGenerator. The overall idea is I'm creating code which will hold data for any type of shape I decide to come up with and want to display to the screen. The problem is my program seems to crash once the first "delete[]" is hit within ShapeData.h in the cleanup() function. Here is the relevant code:
Vertex.h
#pragma once
#include "GLM/glm.hpp"
class Vertex
{
public:
Vertex();
Vertex(glm::vec3 thePosition, glm::vec3 theColor);
glm::vec3 position;
glm::vec3 color;
};
Vertex.cpp
#include "Vertex.h"
Vertex::Vertex()
{}
Vertex::Vertex(glm::vec3 thePosition, glm::vec3 theColor) :
position(thePosition),
color(theColor)
{
}
ShapeData.h
#pragma once
#include "Vertex.h"
#include "GL/glew.h"
struct ShapeData
{
ShapeData() :
verticies(0), numberOfVerts(0),
indicies(0), numberOfIndicies(0)
{}
Vertex* verticies;
GLuint numberOfVerts;
GLushort* indicies;
GLuint numberOfIndicies;
GLsizeiptr VertexBufferSize() const
{
return numberOfVerts * sizeof(Vertex);
}
GLsizeiptr IndexBufferSize() const
{
return numberOfIndicies * sizeof(GLushort);
}
void CleanUp()
{
delete[] verticies;
delete[] indicies;
verticies = 0;
indicies = 0;
numberOfIndicies = 0;
numberOfVerts = 0;
}
};
ShapeGenerator.cpp
#include "ShapeGenerator.h"
ShapeData ShapeGenerator::MakeTriangle()
{
Vertex triangle[] = {
Vertex(glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
Vertex(glm::vec3(-1.0f, -1.0f, 0.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
Vertex(glm::vec3(1.0f, -1.0f, 0.0f), glm::vec3(1.0f, 0.0f, 0.0f))
};
ShapeData shapeData;
shapeData.numberOfVerts = sizeof(triangle) / sizeof(*triangle);
shapeData.verticies = new Vertex[shapeData.numberOfVerts];
memcpy(shapeData.verticies, triangle, sizeof(triangle));
shapeData.verticies = triangle;
GLushort indicies[] = { 0,1,2 };
shapeData.numberOfIndicies = sizeof(indicies) / sizeof(*indicies);
shapeData.indicies = new GLushort[shapeData.numberOfIndicies];
memcpy(shapeData.indicies, indicies, sizeof(indicies));
return shapeData;
}
I'm trying to create a triangle and everything works fine without running the cleanup() function within main. Here is the portion where I'm calling Cleanup() in main:
main.cpp
ShapeData triangle = ShapeGenerator::MakeTriangle();
GLuint bufferID;
glGenBuffers(1, &bufferID);
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
glBufferData(GL_ARRAY_BUFFER, triangle.VertexBufferSize(), triangle.verticies, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (char*)(sizeof(float) * 3));
GLuint indexBufferID;
glGenBuffers(1, &indexBufferID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, triangle.IndexBufferSize(),triangle.indicies, GL_STATIC_DRAW);
triangle.CleanUp();
You are replacing the new[]'d pointer here. This causes a crash because triangle is not new[]'d.
shapeData.verticies = new Vertex[shapeData.numberOfVerts];
memcpy(shapeData.verticies, triangle, sizeof(triangle));
shapeData.verticies = triangle;
I am working on a voxel engine in C++, and after implementing chunks, I realised that they are really expensive to generate. By this, I don't mean populating them with blocks, I mean generating a chunk mesh.
The game runs smoothly once chunks are generated, except for placing and removing voxels. Whenever a chunk changes, it's mesh is reconstructed. This is the expensive process. It takes about 0.36 seconds to do for one chunk, which causes a freeze for about 0.36 seconds when a chunk is edited. Furthermore, because of this 0.36-second spike for a single chunk, loading the world with more than a chunk radius or 3 or 4 takes several minutes. With 4 chunks, it takes 189 seconds, (4*2)^3*0.36 (512 chunks each at 0.36 seconds)
This is my mesh generation code. It iterates over every block in the chunk, and if it is not air, it adds cube vertices for it, else, ignores it. This will later become a more complex method with some stuff I have planned, which is bad if the method is already slow.
void WorldRenderer::constructChunkMesh(Chunk* chunk)
{
if (!chunk->isInitialized() || chunk->getNumBlocks() <= 0)
return; //If the chunk isn't initialized, or is empty, don't construct anything for it.
ChunkMesh mesh;
//iterate over every block within the chunk.
//CHUNK_SIZE has a value of 16. Each chunk is 16x16x16 blocks.
for (int x = 0; x < CHUNK_SIZE; x++)
{
for (int y = 0; y < CHUNK_SIZE; y++)
{
for (int z = 0; z < CHUNK_SIZE; z++)
{
if (chunk->getBlock(x, y, z) != Blocks::BLOCK_TYPE_AIR) //if the block is solid, add vertices, otherwise, don't render it.
{
//the 8 vertices for a cube. mesh.addVertex(...) returns the index.
int i0 = mesh.addVertex(Vertex(glm::vec3(0.0F, 0.0F, 1.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
int i1 = mesh.addVertex(Vertex(glm::vec3(1.0F, 0.0F, 1.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
int i2 = mesh.addVertex(Vertex(glm::vec3(0.0F, 1.0F, 1.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
int i3 = mesh.addVertex(Vertex(glm::vec3(1.0F, 1.0F, 1.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
int i4 = mesh.addVertex(Vertex(glm::vec3(0.0F, 0.0F, 0.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
int i5 = mesh.addVertex(Vertex(glm::vec3(1.0F, 0.0F, 0.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
int i6 = mesh.addVertex(Vertex(glm::vec3(0.0F, 1.0F, 0.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
int i7 = mesh.addVertex(Vertex(glm::vec3(1.0F, 1.0F, 0.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
//The xyz coord in the iteration in world-relative coordinates, instead of chunk-relative
int wx = (chunk->getPos().x * CHUNK_SIZE) + x;
int wy = (chunk->getPos().y * CHUNK_SIZE) + y;
int wz = (chunk->getPos().z * CHUNK_SIZE) + z;
//top y+
if (World::getBlock(wx, wy + 1, wz) <= 0)
{
//if a block does not exist in the y+ direction to this one, add the top face.
mesh.addFace(i2, i3, i7);
mesh.addFace(i2, i7, i6);
}
//bottom y-
if (World::getBlock(wx, wy - 1, wz) <= 0)
{
//if a block does not exist in the y- direction to this one, add the top face.
mesh.addFace(i0, i4, i1);
mesh.addFace(i1, i4, i5);
}
//front z-
if (World::getBlock(wx, wy, wz - 1) <= 0)
{
//if a block does not exist in the z- direction to this one, add the top face.
mesh.addFace(i6, i7, i4);
mesh.addFace(i7, i5, i4);
}
//back z+
if (World::getBlock(wx, wy, wz + 1) <= 0)
{
//if a block does not exist in the z+ direction to this one, add the top face.
mesh.addFace(i0, i1, i2);
mesh.addFace(i1, i3, i2);
}
//right x+
if (World::getBlock(wx + 1, wy, wz) <= 0)
{
//if a block does not exist in the x+ direction to this one, add the top face.
mesh.addFace(i1, i7, i3);
mesh.addFace(i1, i5, i7);
}
//left x-
if (World::getBlock(wx - 1, wy, wz) <= 0)
{
//if a block does not exist in the x- direction to this one, add the top face.
mesh.addFace(i2, i6, i4);
mesh.addFace(i0, i2, i4);
}
}
}
}
}
//The rest of this is OpenGL code, and doesn't add any significant
//performance drop. I have measured this.
GeometryData gd = MeshHandler::compileGeometry(mesh.vertices.data(), mesh.indices.data(), mesh.vertices.size(), mesh.indices.size());
RenderableChunk rc;
rc.pos = chunk->getPos();
auto a = std::find(chunks.begin(), chunks.end(), rc);
int index = a - chunks.begin();
if (a != chunks.end())
{
rc = chunks[index];
}
else
{
GLuint VAO;
GLuint* VBOs = new GLuint[2];
//1527864 bytes maximum per chunk (1.5MB)
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(2, VBOs);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * 8 * MAX_BLOCKS, nullptr, GL_DYNAMIC_DRAW);
glVertexAttribPointer(ATTRIB_VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, position)));
glEnableVertexAttribArray(ATTRIB_VERTEX_ARRAY);
glVertexAttribPointer(ATTRIB_NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, normal)));
glEnableVertexAttribArray(ATTRIB_NORMAL_ARRAY);
glVertexAttribPointer(ATTRIB_COLOUR_ARRAY, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, colour)));
glEnableVertexAttribArray(ATTRIB_COLOUR_ARRAY);
glVertexAttribPointer(ATTRIB_TEXTURE_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, texture)));
glEnableVertexAttribArray(ATTRIB_TEXTURE_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBOs[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * 36 * MAX_BLOCKS, nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
rc.VAO = VAO;
rc.VBOs = VBOs;
}
rc.numIndices = gd.numIndices;
glBindVertexArray(rc.VAO);
glBindBuffer(GL_ARRAY_BUFFER, rc.VBOs[0]);
glBufferSubData(GL_ARRAY_BUFFER, 0, gd.vboSize(), gd.vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rc.VBOs[1]);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, gd.iboSize(), gd.indices);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
if (index >= 0 && index < chunks.size())
{
chunks[index] = rc;
}
else
{
chunks.push_back(rc);
}
}
And the struct ChunkMesh used, where I believe the problem is:
struct ChunkMesh
{
std::vector<Vertex> vertices;
std::vector<GLushort> indices;
int addVertex(Vertex v)
{
//add a vertex to the mesh, and return its index in the list.
vertices.push_back(v);
return vertices.size() - 1;
}
void addFace(int v0, int v1, int v2)
{
//construct a face with 3 vertices.
indices.push_back(v0);
indices.push_back(v1);
indices.push_back(v2);
}
};
I believe the problem is in the ChunkMesh struct, with the push_backs used. The std::vector is very slow for hundreds of push_backs, but I cannot find an alternative. What can I replace the vector with?
Am I going about rendering the chunks entirely wrong? How can I optimise this function?
Any help would be much appreciated.
Thanks.
Edit:
I have tried reserving the vectors, which, to my confusion had no effect on the performance. It remains at 0.36 seconds.
I added a constructor to ChunkMesh to take in the number of blocks, like so:
ChunkMesh(int numBlocks)
{
vertices.reserve(numBlocks * 8); //8 vertices per cube
indices.reserve(numBlocks * 36); //36 indices per cube
}
My recommendation would be to evaluate if you need the vertices which are not at the surface of the chunk.
If not, you do not need to add them to your ChunkMesh, which reduces the number of the vertices and push_back calls noticeable.
I'm having trouble rendering multiple cubes to screen. I have a cube class which sets up the cubes vertices and loads the appropriate shader. The problem that happens is that when multiple instances of my class are created like this:
Cube * cube = new Cube();
Cube * cube1 = new Cube();
And then I initialise the cube objects like so:
cube->setPos(0, 0, 0);
cube->setType(Cube::Type::Green);
cube->createCube();
cube1->setPos(1, 0, 0);
cube1->setType(Cube::Type::Red);
cube1->createCube();
Then I draw the cubes to screen in my render method like so:
cube->draw();
cube1->draw();
My cube class looks like this:
include "cube.h"
Shader* shader;
GLuint tex;
GLuint shaderProgram, fragmentShader, vertexShader;
GLuint vbo, ebo;
GLfloat x, y, z;
GLfloat r, g, b;
Cube::Cube() {
}
void Cube::setPos(GLfloat X, GLfloat Y, GLfloat Z) {
x = X;
y = Y;
z = Z;
}
void Cube::setType(Type type) {
switch (type) {
case Type::Red:
r = 1.0f;
g = 0.0f;
b = 0.0f;
break;
case Type::Green:
r = 0.0f;
g = 1.0f;
b = 0.0f;
break;
case Type::Blue:
r = 0.0f;
g = 1.0f;
b = 0.0f;
break;
default:
r = 0.0f;
g = 0.0f;
b = 0.0f;
break;
}
}
Cube::~Cube() {
glDeleteProgram(shaderProgram);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
glDeleteBuffers(1, &ebo);
glDeleteBuffers(1, &vbo);
}
void Cube::createCube() {
createShader();
GLfloat vertices[] = {
//X, Y, Z, R, G, B
x - 0.5f, y - 0.5f, z - 0.5f, r, g, b, // 0
x + 0.5f, y - 0.5f, z - 0.5f, r, g, b, // 1
x + 0.5f, y + 0.5f, z - 0.5f, r, g, b, // 2
x - 0.5f, y + 0.5f, z - 0.5f, r, g, b, // 3
x - 0.5f, y - 0.5f, z + 0.5f, r, g, b, // 4
x + 0.5f, y - 0.5f, z + 0.5f, r, g, b, // 5
x + 0.5f, y + 0.5f, z + 0.5f, r, g, b, // 6
x - 0.5f, y + 0.5f, z + 0.5f, r, g, b, // 7
};
GLuint elements[] = {
0, 1, 2, 2, 3, 0,
4, 5, 6, 6, 7, 4,
7, 3, 0, 0, 4, 7,
6, 2, 1, 1, 5, 6,
0, 1, 5, 5, 4, 0,
3, 2, 6, 6, 7, 3
};
// Create a Vertex Buffer Object and copy the vertex data to it
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Create an element array
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements,
GL_STATIC_DRAW);
glBindFragDataLocation(shaderProgram, 0, "outColor");
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);
// Specify the layout of the vertex data
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 0);
GLint colAttrib = glGetAttribLocation(shaderProgram, "color");
glEnableVertexAttribArray(colAttrib);
glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat),
(void*) (3 * sizeof(GLfloat)));
}
void Cube::update() {
// Calculate transformation
glm::mat4 trans;
trans = glm::rotate(trans, (float) clock() / (float) CLOCKS_PER_SEC * 1.0f,
glm::vec3(0.0f, 0.0f, 1.0f));
GLint uniTrans = glGetUniformLocation(shaderProgram, "model");
glUniformMatrix4fv(uniTrans, 1, GL_FALSE, glm::value_ptr(trans));
glUniformMatrix4fv(uniTrans, 1, GL_FALSE, glm::value_ptr(trans));
glm::mat4 view = glm::lookAt(glm::vec3(1.2f, 1.2f, 1.2f), glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 0.0f, 1.0f));
GLint uniView = glGetUniformLocation(shaderProgram, "view");
glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(view));
glm::mat4 proj = glm::perspective(45.0f, 800.0f / 600.0f, 1.0f, 10.0f);
GLint uniProj = glGetUniformLocation(shaderProgram, "proj");
glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj));
}
void Cube::draw() {
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
}
void Cube::createShader() {
// load vertex shader source
const GLchar* vertexSource = shader->fileRead("src/shaders/vertex.vs");
if (vertexSource != NULL) std::cout << "vertexSource" << std::endl;
// load fragment shader source
const GLchar* fragmentSource = shader->fileRead("src/shaders/fragment.fs");
if (fragmentSource != NULL) std::cout << "fragmentSource" << std::endl;
// Create and compile the vertex shader
vertexShader = shader->compileShader(vertexSource, GL_VERTEX_SHADER);
// Create and compile the fragment shader
fragmentShader = shader->compileShader(fragmentSource, GL_FRAGMENT_SHADER);
// Link the vertex and fragment shader into a shader program
shaderProgram = shader->compileProgram(vertexShader, fragmentShader);
}
Long story short. I create and initialise two cubes. Both are drawn to screen in code yet when teh compiled program is run only the second cube is shown on screen.
A copy of my full code is available from My GitHub, if you'd like to clone and build it.
I've spent hours upon hours searching for a way to fix this and can't so I'm reaching out to the community. I have a feeling it's something to do with needing a VAO but I can't find out how or where to implement this I've tried a few different ways already.
Any helps greatly appreciated. Thanks in advance!
none of the state you set in the create function will persist beyond another call to it.
First you should move the values to members of the cube class.
Then in create you should create and use a vao:
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
Before specifying the vertex layout.
Then during the draw call:
void Cube::draw() {
glUseProgram(shaderProgram);
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
}
Add a call to glUseProgram(shaderProgram); in update as well.
However it's more efficient to use a single static program and then load the model matrix in the uniform;
void Cube::draw() {
//assume program is already bound and non-cube-specific uniforms are already set
glBindVertexArray(vao);
glUniformMatrix4fv(uniTrans, 1, GL_FALSE, glm::value_ptr(trans));//kept in a field
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
}
Without going into depth on your OpenGL usage (which #ratchetfreak already did in a separate answer), you have a very fundamental C++ problem.
You're declaring a bunch of variables at the start of your Cube.cpp file:
Shader* shader;
GLuint tex;
GLuint shaderProgram, fragmentShader, vertexShader;
GLuint vbo, ebo;
GLfloat x, y, z;
GLfloat r, g, b;
With the variables declared like this, you will have only a single copy of these values, which are shared between all instances of the Cube class. For example, all your cubes will have the same position, which is the one of the last cube you created, because they all share the same x, y and z variables that hold the position.
You need to define these variables as class members so that each Cube instance has separate values. This means that they need to go into the header file, typically in the private section of the class. For example:
class Cube {
public:
// Declaration of constructor, methods, etc.
private:
GLfloat x, y, z;
};
You'll probably want to use more descriptive names for the variables.
The Problem:
It seems as if the second GLuint buffer is not being read in properly.
Update: So the problem must be when I try to input the data to the shader. I rewrote the code (the old code is still below) to use swizzling for the index parameter. That was the only way I could get it to work. I would like to use multiple glVertexAttribPointer's, but every time I try to gives me the same undefined results.
What I Am Trying To Do:
I'm testing out very simple skinned animation with a very simplified shader,
#version 330 core
in vec2 model;
in uint jointID;
const int MAX_JOINTS = 10;
uniform mat4 joints[MAX_JOINTS];
void main()
{
gl_Position = joints[jointID] * vec4(model, 0.0f, 1.0f);
}
I input some simple data,
const GLfloat vertices[] =
{
// upper arm
0.0f, 0.0f,
0.4f, 0.0f,
0.4f, 0.2f,
0.0f, 0.0f,
0.4f, 0.2f,
0.0f, 0.2f,
// lower arm
0.4f, 0.0f,
0.8f, 0.0f,
0.8f, 0.2f,
0.4f, 0.0f,
0.8f, 0.2f,
0.4f, 0.2f
};
const GLuint indices[] =
{
// upper arm
0,
1,
1,
0,
1,
0,
// lower arm
1,
1,
1,
1,
1,
1
};
(The first array containing the vertices and the second array containing the corresponding boneID's.) Oddly, the boneID's never seem too equal 1 because when I make the matrix at index 1 some really funky value, the vertices remain untransformed. This leads me to believe that it is a problem with the way I set up my glVertexAttribPointer's,
void SkinnedModel::draw()
{
shaderProgram.use();
glEnableVertexAttribArray(modelLoc);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexAttribPointer(modelLoc, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(jointIDLoc);
glBindBuffer(GL_ARRAY_BUFFER, indexBuffer);
glVertexAttribPointer(jointIDLoc, 1, GL_UNSIGNED_INT, GL_FALSE, 0, NULL);
glUniformMatrix4fv(jointsLoc, 10, GL_FALSE, (GLfloat*)&poseMatrices);
glDrawArrays(GL_TRIANGLES, 0, numVertices);
glDisableVertexAttribArray(modelLoc);
glDisableVertexAttribArray(jointIDLoc);
}
I've been banging my head against the desk for the past few hours looking at what seems to be correct code. Anyway, it's probably something dumb I missed. Any help is appreciated.
Here is all the relevant source code (just in case):
SkinnedModel.h
#pragma once
#include "stl/DataTypes.h"
#include "Shader.h"
#include <Dense>
using namespace Eigen;
struct Joint
{
Joint** children;
Joint* parent;
U32 index;
};
class SkinnedModel
{
public:
static void init();
static void destroy();
SkinnedModel();
~SkinnedModel();
void create(const GLfloat* vertices, const GLuint* jointIndices, GLint numVertices, Joint* rootJoint);
void draw();
void rotate(Joint* joint, F32 angle, F32 x, F32 y);
GLuint vertexBuffer;
GLuint indexBuffer;
GLint numVertices;
//GLint numJoints;
Joint* root;
Matrix<GLfloat,4,4> poseMatrices[10];
static ShaderProgram shaderProgram;
static GLuint modelLoc;
static GLuint jointIDLoc;
static GLuint modelViewMatrixLoc;
static GLuint jointsLoc;
};
SkinnedModel.cpp
#include "SkinnedModel.h"
ShaderProgram SkinnedModel::shaderProgram;
GLuint SkinnedModel::modelLoc = -1;
GLuint SkinnedModel::jointIDLoc = -1;
GLuint SkinnedModel::modelViewMatrixLoc = -1;
GLuint SkinnedModel::jointsLoc = -1;
void SkinnedModel::init()
{
ShaderProgramSettings shaderPS;
shaderPS.loadShader("Skinned.v.glsl", ShaderType::VERTEX);
shaderPS.loadShader("Skinned.f.glsl", ShaderType::FRAGMENT);
shaderProgram = shaderPS.create();
shaderProgram.use();
modelLoc = shaderProgram.getAttrib("model");
jointIDLoc = shaderProgram.getAttrib("jointID");
//modelViewMatrixLoc = shaderProgram.getUniform("modelViewMatrix");
jointsLoc = shaderProgram.getUniform("joints");
}
void SkinnedModel::destroy()
{
shaderProgram.destroy();
}
SkinnedModel::SkinnedModel()
{
}
SkinnedModel::~SkinnedModel()
{
glDeleteBuffers(1, &vertexBuffer);
glDeleteBuffers(1, &indexBuffer);
}
void SkinnedModel::create(const GLfloat* vertices, const GLuint* jointIndices, GLint numVertices, Joint* rootJoint)
{
this->numVertices = numVertices;
this->root = rootJoint;
for(U32 i=0;i<10;++i)
{
poseMatrices[i] = Matrix<GLfloat,4,4>::Identity();
}
poseMatrices[1] = Matrix<GLfloat,4,4>::Zero(); // <--- This should mess it up!
// Creating buffers
glDeleteBuffers(1, &vertexBuffer);
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, numVertices*2*sizeof(GLfloat), vertices, GL_STATIC_DRAW);
glDeleteBuffers(1, &indexBuffer);
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ARRAY_BUFFER, numVertices*sizeof(GLuint), jointIndices, GL_STATIC_DRAW);
}
void SkinnedModel::draw()
{
shaderProgram.use();
glEnableVertexAttribArray(modelLoc);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexAttribPointer(modelLoc, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(jointIDLoc);
glBindBuffer(GL_ARRAY_BUFFER, indexBuffer);
glVertexAttribPointer(jointIDLoc, 1, GL_UNSIGNED_INT, GL_FALSE, 0, NULL);
glUniformMatrix4fv(jointsLoc, 10, GL_FALSE, (GLfloat*)&poseMatrices);
glDrawArrays(GL_TRIANGLES, 0, numVertices);
glDisableVertexAttribArray(modelLoc);
glDisableVertexAttribArray(jointIDLoc);
}
void SkinnedModel::rotate(Joint* joint, F32 angle, F32 x, F32 y)
{
F32 rcos = cos(angle);
F32 rsin = sin(angle);
Matrix<GLfloat, 4, 4> rotMatrix = Matrix<GLfloat, 4, 4>::Identity();
rotMatrix(0,0) = rcos;
rotMatrix(0,1) = -rsin;
rotMatrix(1,0) = rsin;
rotMatrix(1,1) = rcos;
rotMatrix(0,3) = x-rcos*x+rsin*y;
rotMatrix(1,3) = y-rsin*x-rcos*y;
poseMatrices[joint->index] *= rotMatrix;
}
Game.cpp
void Game::init()
{
GUI::init();
SkinnedModel::init();
getScreen()->setBackgroundColor(1.0f, 1.0f, 1.0f);
const GLfloat vertices[] =
{
// upper arm
0.0f, 0.0f,
0.4f, 0.0f,
0.4f, 0.2f,
0.0f, 0.0f,
0.4f, 0.2f,
0.0f, 0.2f,
// lower arm
0.4f, 0.0f,
0.8f, 0.0f,
0.8f, 0.2f,
0.4f, 0.0f,
0.8f, 0.2f,
0.4f, 0.2f
};
const GLuint indices[] =
{
// upper arm
0,
1,
1,
0,
1,
0,
// lower arm
1,
1,
1,
1,
1,
1
};
upperArm.parent = 0;
upperArm.children = new Joint*[1];
upperArm.children[0] = &lowerArm;
upperArm.index = 0;
lowerArm.parent = &upperArm;
lowerArm.children = 0;
lowerArm.index = 1;
m.create(vertices, indices, 12, &upperArm);
//m.rotate(&lowerArm, PI/4, 0.4f, 0.1f);
//DEBUG("SIZE %i", sizeof(Eigen::Matrix<GLfloat,4,4>));
}
void Game::loop(double dt)
{
m.draw();
}
Update: Apparently if all values for the boneID are set to 1 it never uses 1 in the shader either #.#. So the second array isn't even being read, or it's not being read correctly.
You need to use glVertexAttribIPointer if you are using integer vertex attributes (i.e. something you have declared in uint or in int etc. in your vertex shader).
replace
glVertexAttribPointer(jointIDLoc, 1, GL_UNSIGNED_INT, GL_FALSE, 0, NULL);
with
glVertexAttribIPointer(jointIDLoc, 1, GL_UNSIGNED_INT, 0, NULL);
(note that glVertexAttribIPointer doesn't take the normalized parameter)