C++ Create a cube with sections - c++

I'm trying to figure out an algorithm to create a cube/box where each dimension can be divided into sections. Something similar to rings and sides when creating a sphere or a cylinder.
For example to get all the vertices for a sphere at once one can do:
for (int r = 0; r < rings-1; ++r)
{
float u = -M_PI_2 + ((r+1) * M_PI / rings);
float v = -M_PI;
for (int s= 0; s < sides; ++s)
{
float x = radius * cos(u) * cos(v);
float y = radius * sin(u);
float z = radius * cos(u) * sin(v);
add_vertex(x, y, z);
v += 2 * M_PI / sides;
}
}
Any help on how to go about doing something like that in theory would be appreciated.

Here is code wich create cude with stacks, wslices and dslices. It was used to draw array buffers to OpenGL. You can simply remove unused normal and color array and use only vertex array. And do not be affraid because of GLint and GLfloat types they are only int and float synonyms.
void DrawBox(GLfloat fWidth,GLfloat fHeight,GLfloat fDepth,GLint wslices,GLint dslices,GLint stacks)
{
// Calculate number of primitives on each side of box
// because we can use different tessalation configurations
// we must calculate separate group of box sides
int iTopButtonQuads = wslices * dslices * 2; // Calculate number of quads in top and button sides
int iLeftRightQuads = dslices * stacks * 2; // Calculate number of quads in left and right sides
int iFrontBackQuads = wslices * stacks * 2; // Calculate number of quads in front and back sides
// If we consider to use quads as primitive then each primitive will
// have 4 points, and each point has color, coord and normal attribute.
// So we create separate array to contain each attibute values.
float* pfVertices = new float[(iTopButtonQuads + iLeftRightQuads + iFrontBackQuads) * 3 * 4];
float* pfColors = new float[(iTopButtonQuads + iLeftRightQuads + iFrontBackQuads) * 3 * 4];
float* pfNormals = new float[(iTopButtonQuads + iLeftRightQuads + iFrontBackQuads) * 3 * 4];
int iVertexIndex = 0;
GLfloat Xstep = fWidth / wslices;
GLfloat Ystep = fHeight / stacks;
GLfloat Zstep = fDepth / dslices;
GLfloat firstX = fWidth / 2.0f;
GLfloat firstY = fHeight / 2.0f;
GLfloat firstZ = fDepth / 2.0f;
GLfloat currX = 0.0f;
GLfloat currY = 0.0f;
GLfloat currZ = 0.0f;
GLfloat x_status = 0.0f;
GLfloat y_status = 0.0f;
GLfloat z_status = 0.0f;
// the bottom and the top of the box
for (currZ = -firstZ, z_status = 0.0f; currZ < firstZ - Zstep / 2.0f; currZ += Zstep, z_status += Zstep)
{
for (currX = -firstX, x_status = 0.0f; currX < firstX - Xstep / 2.0f; currX += Xstep, x_status += Xstep)
{
int iCurrentIndex = iVertexIndex * 3 * 4;
float pfNormal[3] = { 0.0f, -1.0f, 0.0f };
memcpy(pfNormals + iCurrentIndex, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 3, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 6, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 9, pfNormal, 3 * 4);
float pfColor[3] = { 1.0f, 0.0f, 0.0f };
memcpy(pfColors + iCurrentIndex, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 3, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 6, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 9, pfColor, 3 * 4);
float pfVertex0[3] = {currX,-firstY,currZ};
float pfVertex1[3] = {currX + Xstep,-firstY,currZ};
float pfVertex2[3] = {currX + Xstep,-firstY,currZ + Zstep};
float pfVertex3[3] = {currX,-firstY,currZ + Zstep};
memcpy(pfVertices + iCurrentIndex, pfVertex0, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 3, pfVertex1, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 6, pfVertex2, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 9, pfVertex3, 3 * 4);
iVertexIndex++;
}
for (currX = -firstX, x_status = 0.0f; currX < firstX - Xstep / 2.0f; currX += Xstep, x_status += Xstep)
{
int iCurrentIndex = iVertexIndex * 3 * 4;
float pfNormal[3] = { 0.0f, 1.0f, 0.0f };
memcpy(pfNormals + iCurrentIndex, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 3, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 6, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 9, pfNormal, 3 * 4);
float pfColor[3] = { 0.0f, 1.0f, 0.0f };
memcpy(pfColors + iCurrentIndex, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 3, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 6, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 9, pfColor, 3 * 4);
float pfVertex0[3] = {currX + Xstep,firstY,currZ + Zstep};
float pfVertex1[3] = {currX + Xstep,firstY,currZ};
float pfVertex2[3] = {currX,firstY,currZ};
float pfVertex3[3] = {currX,firstY,currZ + Zstep};
memcpy(pfVertices + iCurrentIndex, pfVertex0, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 3, pfVertex1, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 6, pfVertex2, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 9, pfVertex3, 3 * 4);
iVertexIndex++;
}
}
// the front and the back of the box
for (currY = -firstY, y_status = 0.0f; currY < firstY - Ystep / 2.0f ; currY += Ystep, y_status += Ystep)
{
for (currX = -firstX, x_status = 0.0f; currX < firstX - Xstep / 2.0f; currX += Xstep, x_status += Xstep)
{
int iCurrentIndex = iVertexIndex * 3 * 4;
float pfNormal[3] = { 0.0f, 0.0f, 1.0f };
memcpy(pfNormals + iCurrentIndex, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 3, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 6, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 9, pfNormal, 3 * 4);
float pfColor[3] = { 0.0f, 0.0f, 1.0f };
memcpy(pfColors + iCurrentIndex, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 3, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 6, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 9, pfColor, 3 * 4);
float pfVertex0[3] = {currX,currY,firstZ};
float pfVertex1[3] = {currX + Xstep,currY,firstZ};
float pfVertex2[3] = {currX + Xstep,currY + Ystep,firstZ};
float pfVertex3[3] = {currX,currY + Ystep,firstZ};
memcpy(pfVertices + iCurrentIndex, pfVertex0, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 3, pfVertex1, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 6, pfVertex2, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 9, pfVertex3, 3 * 4);
iVertexIndex++;
}
for (currX = -firstX, x_status = 0.0f; currX < firstX - Xstep / 2.0f; currX += Xstep, x_status += Xstep)
{
int iCurrentIndex = iVertexIndex * 3 * 4;
float pfNormal[3] = { 0.0f, 0.0f, -1.0f };
memcpy(pfNormals + iCurrentIndex, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 3, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 6, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 9, pfNormal, 3 * 4);
float pfColor[3] = { 0.0f, 1.0f, 1.0f };
memcpy(pfColors + iCurrentIndex, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 3, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 6, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 9, pfColor, 3 * 4);
float pfVertex0[3] = {currX + Xstep,currY + Ystep,-firstZ};
float pfVertex1[3] = {currX + Xstep,currY,-firstZ};
float pfVertex2[3] = {currX,currY,-firstZ};
float pfVertex3[3] = {currX,currY + Ystep,-firstZ};
memcpy(pfVertices + iCurrentIndex, pfVertex0, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 3, pfVertex1, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 6, pfVertex2, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 9, pfVertex3, 3 * 4);
iVertexIndex++;
}
}
// Right side and the left side of the box
for (currY = -firstY, y_status = 0.0f; currY < firstY - Ystep / 2.0f; currY += Ystep, y_status += Ystep)
{
for (currZ = -firstZ, z_status = 0.0f; currZ < firstZ - Zstep / 2.0f; currZ += Zstep, z_status += Zstep)
{
int iCurrentIndex = iVertexIndex * 3 * 4;
float pfNormal[3] = { 1.0f, 0.0f, 0.0f };
memcpy(pfNormals + iCurrentIndex, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 3, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 6, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 9, pfNormal, 3 * 4);
float pfColor[3] = { 1.0f, 0.0f, 1.0f };
memcpy(pfColors + iCurrentIndex, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 3, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 6, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 9, pfColor, 3 * 4);
float pfVertex0[3] = {firstX,currY,currZ};
float pfVertex1[3] = {firstX,currY + Ystep,currZ};
float pfVertex2[3] = {firstX,currY + Ystep,currZ + Zstep};
float pfVertex3[3] = {firstX,currY,currZ + Zstep};
memcpy(pfVertices + iCurrentIndex, pfVertex0, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 3, pfVertex1, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 6, pfVertex2, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 9, pfVertex3, 3 * 4);
iVertexIndex++;
}
for (currZ = -firstZ, z_status = 0.0f; currZ < firstZ - Zstep / 2.0f; currZ += Zstep, z_status += Zstep)
{
int iCurrentIndex = iVertexIndex * 3 * 4;
float pfNormal[3] = { -1.0f, 0.0f, 0.0f };
memcpy(pfNormals + iCurrentIndex, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 3, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 6, pfNormal, 3 * 4);
memcpy(pfNormals + iCurrentIndex + 9, pfNormal, 3 * 4);
float pfColor[3] = { 1.0f, 1.0f, 0.0f };
memcpy(pfColors + iCurrentIndex, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 3, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 6, pfColor, 3 * 4);
memcpy(pfColors + iCurrentIndex + 9, pfColor, 3 * 4);
float pfVertex0[3] = {-firstX,currY,currZ};
float pfVertex1[3] = {-firstX,currY,currZ + Zstep};
float pfVertex2[3] = {-firstX,currY + Ystep,currZ + Zstep};
float pfVertex3[3] = {-firstX,currY + Ystep,currZ};
memcpy(pfVertices + iCurrentIndex, pfVertex0, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 3, pfVertex1, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 6, pfVertex2, 3 * 4);
memcpy(pfVertices + iCurrentIndex + 9, pfVertex3, 3 * 4);
iVertexIndex++;
}
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glColorPointer(3, GL_FLOAT, 0, (void*)pfColors);
glNormalPointer(GL_FLOAT, 0, (void*)pfNormals);
glVertexPointer(3, GL_FLOAT, 0, (void*)pfVertices);
glDrawArrays(GL_QUADS, 0, (iTopButtonQuads + iLeftRightQuads + iFrontBackQuads) * 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
delete [] pfVertices;
delete [] pfNormals;
delete [] pfColors;
}
here is the code to optimize vertex arrays.
// 1254 Verticies
// 2141 Texture Coordinates
// 1227 Normals
// 2248 Triangles
static short face_indicies[2248][9] = {
// Object #-1
{0,15,14 ,0,1,2 ,0,1,2 }, {0,1,15 ,0,3,1 ,0,3,1 }, {1,16,15 ,3,4,1 ,3,4,1 },
{1,2,16 ,3,5,4 ,3,5,4 }, {2,17,16 ,5,6,4 ,5,6,4 }, {2,3,17 ,5,7,6 ,5,7,6 },
{3,18,17 ,7,8,6 ,7,8,6 }, {3,4,18 ,7,9,8 ,7,9,8 }, {4,19,18 ,9,10,8 ,9,10,8 },
//.................................................................
};
static GLfloat vertices [1254][3] = {
{1.32715f,-1.99755f,-0.614826f},{1.32715f,-2.20819f,-0.343913f},{1.32715f,-2.5155f,-0.191263f},
{1.32715f,-2.85867f,-0.187049f},{1.32715f,-3.16964f,-0.332104f},{1.32715f,-3.38686f,-0.597763f},
{1.32715f,-3.46734f,-0.931359f},{1.32715f,-3.39508f,-1.26683f},{1.32715f,-3.18445f,-1.53774f},
//..................................................................
};
static GLfloat normals [1227][3] = {
{-0.45634f,0.376195f,-0.80637f},{0.456348f,0.688811f,-0.563281f},{0.45634f,0.376194f,-0.80637f},
{-0.456348f,0.688811f,-0.563281f},{0.456341f,0.865005f,-0.208615f},{-0.456341f,0.865005f,-0.208615f},
{0.456341f,0.869868f,0.187303f},{-0.456341f,0.869868f,0.187303f},{0.456349f,0.702436f,0.546196f},
//..................................................................
};
static GLfloat textures [2141][2] = {
{0.94929f,0.497934f},{0.99452f,0.477509f},{0.994669f,0.497506f},
{0.949142f,0.47796f},{0.994339f,0.457508f},{0.948961f,0.457992f},
};
////////////////////////////////////////////////////////////////
// These are hard coded for this particular example
GLushort uiIndexes[2248*3]; // Maximum number of indexes
GLfloat vVerts[2248*3][3]; // (Worst case scenario)
GLfloat vText[2248*3][2];
GLfloat vNorms[2248*3][3];
int iLastIndex = 0; // Number of indexes actually used
/////////////////////////////////////////////////////////////////
// Compare two floating point values and return true if they are
// close enough together to be considered the same.
int IsSame(float x, float y, float epsilon)
{
if(fabs(x-y) < epsilon)
return 1;
return 0;
}
///////////////////////////////////////////////////////////////
// Goes through the arrays and looks for duplicate verticies
// that can be shared. This expands the original array somewhat
// and returns the number of true unique verticies that now
// populates the vVerts array.
int IndexTriangles(void)
{
int iFace, iPoint, iMatch;
float e = 0.000001; // How small a difference to equate
// LOOP THROUGH all the faces
int iIndexCount = 0;
for(iFace = 0; iFace < 2248; iFace++)
{
for(iPoint = 0; iPoint < 3; iPoint++)
{
// Search for match
for(iMatch = 0; iMatch < iLastIndex; iMatch++)
{
// If Vertex is the same...
if(IsSame(vertices[face_indicies[iFace][iPoint]][0], vVerts[iMatch][0], e) &&
IsSame(vertices[face_indicies[iFace][iPoint]][1], vVerts[iMatch][1], e) &&
IsSame(vertices[face_indicies[iFace][iPoint]][2], vVerts[iMatch][2], e) &&
// AND the Normal is the same...
IsSame(normals[face_indicies[iFace][iPoint+3]][0], vNorms[iMatch][0], e) &&
IsSame(normals[face_indicies[iFace][iPoint+3]][1], vNorms[iMatch][1], e) &&
IsSame(normals[face_indicies[iFace][iPoint+3]][2], vNorms[iMatch][2], e) &&
// And Texture is the same...
IsSame(textures[face_indicies[iFace][iPoint+6]][0], vText[iMatch][0], e) &&
IsSame(textures[face_indicies[iFace][iPoint+6]][1], vText[iMatch][1], e))
{
// Then add the index only
uiIndexes[iIndexCount] = iMatch;
iIndexCount++;
break;
}
}
// No match found, add this vertex to the end of our list, and update the index array
if(iMatch == iLastIndex)
{
// Add data and new index
memcpy(vVerts[iMatch], vertices[face_indicies[iFace][iPoint]], sizeof(float) * 3);
memcpy(vNorms[iMatch], normals[face_indicies[iFace][iPoint+3]], sizeof(float) * 3);
memcpy(vText[iMatch], textures[face_indicies[iFace][iPoint+6]], sizeof(float) * 2);
uiIndexes[iIndexCount] = iLastIndex;
iIndexCount++;
iLastIndex++;
}
}
}
return iIndexCount;
}
/////////////////////////////////////////////
// Function to stitch the triangles together
// and draw the ship
void DrawModel(void)
{
static int iIndexes = 0;
char cBuffer[32];
// The first time this is called, reindex the triangles. Report the results
// in the window title
if(iIndexes == 0)
{
iIndexes = IndexTriangles();
sprintf(cBuffer,"Verts = %d Indexes = %d", iLastIndex, iIndexes);
glutSetWindowTitle(cBuffer);
}
// Use vertices, normals, and texture coordinates
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// Here's where the data is now
glVertexPointer(3, GL_FLOAT,0, vVerts);
glNormalPointer(GL_FLOAT, 0, vNorms);
glTexCoordPointer(2, GL_FLOAT, 0, vText);
// Draw them
glDrawElements(GL_TRIANGLES, iIndexes, GL_UNSIGNED_SHORT, uiIndexes);
}
This algo will works with arbitrary mesh array. Or just place unique values it means remove the values where curr + step occurs so must be left 1 vertex per loop body not all 4. Example
Where we have 4 vertices
float pfVertex0[3] = {-firstX,currY,currZ};
float pfVertex1[3] = {-firstX,currY,currZ + Zstep};
float pfVertex2[3] = {-firstX,currY + Ystep,currZ + Zstep};
float pfVertex3[3] = {-firstX,currY + Ystep,currZ};
The only vertex left will be
float pfVertex0[3] = {-firstX,currY,currZ};
The simple arithmetic shows 24 / 4 = 6 != 8
so to form proper box we just must add extra iteration to 1 side of the box (2 vertices).

Related

Torus is vertical and I need it to be horizontal [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 5 days ago.
Improve this question
I am unsure why this torus is going vertical and not horizontal:
I've tried adjusting float currentAngle but that just spins it on it's axis, it doesn't lay it down. I don't think it's the glm::translate() settings for that is just moving the object on the x,y,and z axis.
// Roll of Tape
ShapeData ShapeGenerator::makeTapeRoll(uint tesselation)
{
ShapeData ret;
ret.numVertices = tesselation * 4;
ret.vertices = new Vertex[ret.numVertices];
float cylinderHeight = 0.05f;
float cylinderRadius = 0.05f;
float sphereRadius = cylinderRadius * 1.5f;
float currentAngle = -45.0f;
float angleIncrement = (2.0f * PI) / tesselation;
for (uint i = 0; i < tesselation; i++)
{
float x1 = sphereRadius * cos(currentAngle);
float y1 = sphereRadius * sin(currentAngle);
float x2 = cylinderRadius * cos(currentAngle);
float y2 = cylinderRadius * sin(currentAngle);
ret.vertices[i].position = vec3(x1, y1, sphereRadius);
ret.vertices[i].color = randomColor();
ret.vertices[i + tesselation].position = vec3(x2, y2, sphereRadius + cylinderHeight);
ret.vertices[i + tesselation].color = randomColor();
ret.vertices[i + tesselation * 2].position = vec3(x2, y2, sphereRadius);
ret.vertices[i + tesselation * 2].color = randomColor();
ret.vertices[i + tesselation * 3].position = vec3(x1, y1, sphereRadius - cylinderHeight);
ret.vertices[i + tesselation * 3].color = randomColor();
currentAngle += angleIncrement;
}
ret.numIndices = tesselation * 6;
ret.indices = new GLushort[ret.numIndices];
for (uint i = 0; i < tesselation; i++)
{
ret.indices[i * 6] = i;
ret.indices[i * 6 + 1] = (i + 1) % tesselation;
ret.indices[i * 6 + 2] = i + tesselation;
ret.indices[i * 6 + 3] = (i + 1) % tesselation;
ret.indices[i * 6 + 4] = (i + 1) % tesselation + tesselation;
ret.indices[i * 6 + 5] = i + tesselation;
}
return ret;
}
ShapeData tapeRoll = ShapeGenerator::makeTapeRoll(64);
unsigned int tapeRollIndexByteOffset = 0;
unsigned int tapeRollNumIndices = 0;
unsigned int tapeRollVBO{}, tapeRollVAO;
glGenVertexArrays(1, &tapeRollVAO);
glGenBuffers(1, &tapeRollVBO);
glBindVertexArray(tapeRollVAO);
glBindBuffer(GL_ARRAY_BUFFER, tapeRollVBO);
glBufferData(GL_ARRAY_BUFFER, tapeRoll.vertexBufferSize() + tapeRoll.indexBufferSize(), 0, GL_STATIC_DRAW);
currentOffset = 0;
glBufferSubData(GL_ARRAY_BUFFER, currentOffset, tapeRoll.vertexBufferSize(), tapeRoll.vertices);
currentOffset += tapeRoll.vertexBufferSize();
tapeRollIndexByteOffset = currentOffset;
glBufferSubData(GL_ARRAY_BUFFER, currentOffset, tapeRoll.indexBufferSize(), tapeRoll.indices);
tapeRollNumIndices = tapeRoll.numIndices;
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, VERTEX_BYTE_SIZE, (void*)0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, VERTEX_BYTE_SIZE, (void*)(sizeof(float) * 3));
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, VERTEX_BYTE_SIZE, (void*)(sizeof(float) * 6));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tapeRollVBO);
// setup to draw tapeRoll
glBindTexture(GL_TEXTURE_2D, tapeDiffuseMap);
glBindVertexArray(tapeRollVAO);
model = model = glm::mat4(0.8f);
model = glm::translate(model, glm::vec3(2.3f, -0.3f, 3.0f));
model = glm::scale(model, glm::vec3(3.0f));
lightingShader.setMat4("model", model);
// draw tapeRoll
glDrawElements(GL_TRIANGLES, tapeRollNumIndices, GL_UNSIGNED_SHORT, (void*)tapeRollIndexByteOffset);

OpenGL Object rotation using your own Matrix class in C++

I am playing around with OpenGL and one thing I decided to do is create my own Matrix class, instead of using glm's matrices.
The Matrix class has methods for translating, rotating and scaling the object, which are written below:
Matrix4 Matrix4::translate(Matrix4& matrix, Vector3& translation)
{
Vector4 result(translation, 1.0f);
result.multiply(matrix);
matrix.mElements[3 * 4 + 0] = result.x;
matrix.mElements[3 * 4 + 1] = result.y;
matrix.mElements[3 * 4 + 2] = result.z;
return matrix;
}
Matrix4 Matrix4::rotate(Matrix4& matrix, float angle, Vector3& axis)
{
if (axis.x == 0 && axis.y == 0 && axis.z == 0)
return matrix;
float r = angle;
float s = sin(r);
float c = cos(r);
float omc = 1.0f - cos(r);
float x = axis.x;
float y = axis.y;
float z = axis.z;
matrix.mElements[0 + 0 * 4] = c + x * x * omc;
matrix.mElements[1 + 0 * 4] = x * y * omc - z * s;
matrix.mElements[2 + 0 * 4] = z * x * omc + y * s;
matrix.mElements[0 + 1 * 4] = x * y * omc + z * s;
matrix.mElements[1 + 1 * 4] = c + y * y * omc;
matrix.mElements[2 + 1 * 4] = z * y * omc - x * s;
matrix.mElements[0 + 2 * 4] = x * z * omc - y * s;
matrix.mElements[1 + 2 * 4] = y * z * omc + x * s;
matrix.mElements[2 + 2 * 4] = c + z * z * omc;
return matrix;
}
Matrix4 Matrix4::scale(Matrix4& matrix, Vector3& scaler)
{
matrix.mElements[0 + 0 * 4] *= scaler.x;
matrix.mElements[1 + 0 * 4] *= scaler.x;
matrix.mElements[2 + 0 * 4] *= scaler.x;
matrix.mElements[0 + 1 * 4] *= scaler.y;
matrix.mElements[1 + 1 * 4] *= scaler.y;
matrix.mElements[2 + 1 * 4] *= scaler.y;
matrix.mElements[0 + 2 * 4] *= scaler.z;
matrix.mElements[1 + 2 * 4] *= scaler.z;
matrix.mElements[2 + 2 * 4] *= scaler.z;
matrix.mElements[3 + 3 * 4] = 1;
return matrix;
}
When I call the translate, rotate and scale methods in while loop (in this particular order), it does what I want, which is translate the object, then rotate it around its local origin and scale it. However, when I want to switch order so I call rotation first and then translation, I want it to do this:
But my code dosen't do that. Instead, its doing this:
What can I do so that my object only rotates around the center of the screen and not around it's local origin aswell?
My only guess is that I am doing something wrong with adding the rotation calculation on transformed matrix, but I still can't tell what it is.
EDIT: One thing i need to point out is if i left out the rotation method and i only tackle with translation and scaling, they do what i expect them to do in translation first, rotation second and in rotation first, translation second order.
EDIT 2: Here is how i call these functions in while loop.
Matrix4 trans = Matrix4(1.0f);
trans = Matrix4::rotate(trans, (float)glfwGetTime(), Vector3(0.0f, 0.0f, 1.0f));
trans = Matrix4::translate(trans, Vector3(0.5f, -0.5f, 0.0f));
trans = Matrix4::scale(trans, Vector3(0.5f, 0.5f, 1.0f));
shader.setUniformMatrix4f("uTransform", trans);
You have to concatenate the matrices by a matrix multiplication.
A matrix multiplication C = A * B works like this:
Matrix4x4 A, B, C;
// C = A * B
for ( int k = 0; k < 4; ++ k )
for ( int j = 0; j < 4; ++ j )
C[k][j] = A[0][j] * B[k][0] + A[1][j] * B[k][1] + A[2][j] * B[k][2] + A[3][j] * B[k][3];
I recommend to create specify the matrix class somehow like this:
#include <array>
class Matrix4
{
public:
std::array<float, 16> mElements{
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 };
const float * dataPtr( void ) const { return mElements.data(); }
Matrix4 & multiply( const Matrix4 &mat );
Matrix4 & translate( const Vector3 &translation );
Matrix4 & scale( const Vector3 &scaler );
Matrix4 & rotate( float angle, const Vector3 &axis );
};
Implement the matrix multiplication. Note, you have to store the result in a buffer.
If you would write the result back to the matrix member directly, then you would change elements, which will read again later in the nested loop and the result wouldn't be correct:
Matrix4& Matrix4::multiply( const Matrix4 &mat )
{
// multiply the existing matrix by the new and store the result in a buffer
const float *A = dataPtr();
const float *B = mat.dataPtr();
std::array<float, 16> C;
for ( int k = 0; k < 4; ++ k ) {
for ( int j = 0; j < 4; ++ j ) {
C[k*4+j] =
A[0*4+j] * B[k*4+0] +
A[1*4+j] * B[k*4+1] +
A[2*4+j] * B[k*4+2] +
A[3*4+j] * B[k*4+3];
}
}
// copy the buffer to the attribute
mElements = C;
return *this;
}
Adapt the methods for translation, rotation and scaling like this:
Matrix4 & Matrix4::translate( const Vector3 &translation )
{
float x = translation.x;
float y = translation.y;
float z = translation.z;
Matrix4 transMat;
transMat.mElements = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
x, y, z, 1.0f };
return multiply(transMat);
}
Matrix4 & Matrix4::rotate( float angle, const Vector3 &axis )
{
float x = axis.x;
float y = axis.y;
float z = axis.z;
float c = cos(angle);
float s = sin(angle);
Matrix4 rotationMat;
rotationMat.mElements = {
x*x*(1.0f-c)+c, x*y*(1.0f-c)-z*s, x*z*(1.0f-c)+y*s, 0.0f,
y*x*(1.0f-c)+z*s, y*y*(1.0f-c)+c, y*z*(1.0f-c)-x*s, 0.0f,
z*x*(1.0f-c)-y*s, z*y*(1.0f-c)+x*s, z*z*(1.0f-c)+c, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
return multiply(rotationMat);
}
Matrix4 & Matrix4::scale( const Vector3 &scaler )
{
float x = scaler.x;
float y = scaler.y;
float z = scaler.z;
Matrix4 scaleMat;
scaleMat.mElements = {
x, 0.0f, 0.0f, 0.0f,
0.0f, y, 0.0f, 0.0f,
0.0f, 0.0f, z, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
return multiply(scaleMat);
}
If you use the matrix class like this,
float angle_radians = ....;
Vector3 scaleVec{ 0.2f, 0.2f, 0.2f };
Vector3 transVec{ 0.3f, 0.3f, 0.0f };
Vector3 rotateVec{ 0.0f, 0.0f, 1.0f };
Matrix4 model;
model.rotate( angle_rad, rotateVec );
model.translate( transVec );
model.scale( scaleVec );
then the result would look like this:
The function rotate() isn't performing an actual rotation. Only generating a partial rotation matrix, and overwriting it over the original matrix.
You need to construct a complete one and multiply it to the original matrix.
Matrix4 Matrix4::rotate(const Matrix4& matrix, float angle, const Vector3& axis)
{
if (axis.x == 0 && axis.y == 0 && axis.z == 0)
return matrix;
float r = angle;
float s = sin(r);
float c = cos(r);
float omc = 1.0f - cos(r);
float x = axis.x;
float y = axis.y;
float z = axis.z;
Matrix4 r;
r.mElements[0 + 0 * 4] = c + x * x * omc;
r.mElements[1 + 0 * 4] = x * y * omc - z * s;
r.mElements[2 + 0 * 4] = z * x * omc + y * s;
r.mElements[3 + 0 * 4] = 0;
r.mElements[0 + 1 * 4] = x * y * omc + z * s;
r.mElements[1 + 1 * 4] = c + y * y * omc;
r.mElements[2 + 1 * 4] = z * y * omc - x * s;
r.mElements[3 + 1 * 4] = 0;
r.mElements[0 + 2 * 4] = x * z * omc - y * s;
r.mElements[1 + 2 * 4] = y * z * omc + x * s;
r.mElements[2 + 2 * 4] = c + z * z * omc;
r.mElements[3 + 2 * 4] = 0;
r.mElements[0 + 3 * 4] = 0;
r.mElements[1 + 3 * 4] = 0;
r.mElements[2 + 3 * 4] = 0;
r.mElements[3 + 3 * 4] = 1;
return r * matrix;
}

How to calculate ScreenToWorld coordinates?

I have an object that contains my view state, and I wish to be able to convert from world to screen coordinates, which works. However I also want to be able to convert from screen to world coordinates, I have the following code:
#include <glm/glm.hpp>
#include <gmock/gmock.h>
class CoordinateSpace
{
public:
CoordinateSpace(int w, int h)
{
mW = w;
mH = h;
}
glm::vec2 WorldToScreen(const glm::vec2& worldPos)
{
return ((mProjection * mView) * glm::vec4(worldPos, 1, 1)) * glm::vec4(mW / 2, -mH / 2, 1, 1) + glm::vec4(mW / 2, mH / 2, 0, 0);
}
glm::vec2 ScreenToWorld(const glm::vec2& screenPos)
{
return (glm::inverse(mProjection * mView) * glm::vec4(screenPos, 1, 1)) * glm::vec4(mW / 2, -mH / 2, 1, 1) + glm::vec4(mW / 2, mH / 2, 0, 0);
}
void UpdateCamera()
{
glm::mat4 target_projection = glm::ortho(
-mScreenSize.x / 2.0f,
mScreenSize.x / 2.0f,
mScreenSize.y / 2.0f,
-mScreenSize.y / 2.0f,
-1.0f,
1.0f);
glm::mat4 camMat = glm::translate(glm::mat4(1.0f), glm::vec3(-mCameraPosition, 0));
mView = camMat;
mProjection = target_projection;
}
glm::vec2 mScreenSize = glm::vec2;
glm::vec2 mCameraPosition = glm::vec2;
protected:
int mW = 0;
int mH = 0;
// 2d ortho projection
glm::mat4 mProjection;
// camera location into the world
glm::mat4 mView;
};
TEST(CoordinateSpace, Conversion)
{
CoordinateSpace coords(640, 480);
coords.mCameraPosition = { 0.0f, 0.0f };
coords.mScreenSize = { 640.0f, 480.0f };
coords.UpdateCamera();
const glm::vec2 actual1 = coords.WorldToScreen({ 50.0f, 100.0f });
ASSERT_EQ(glm::round((640.0f/2)+50.0f), glm::round(actual1.x));
ASSERT_EQ(glm::round((480.0f/2)+100.0f), glm::round(actual1.y));
const glm::vec2 actual2 = coords.ScreenToWorld(actual1);
ASSERT_EQ(glm::round(50.0f), glm::round(actual2.x));
ASSERT_EQ(glm::round(100.0f), glm::round(actual2.y));
}
Instead of 50.0f I get 5.12032e+06, how do I correctly calculate ScreenToWorld?
Given that you have:
SCREENPOS = ((PROJ * VIEW) * WORLDPOS) * A + B;
Using algebra to isolate WORLDPOS, I'd presume this would work:
WORLDPOS = ((SCREENPOS - B) / A) * INV(PROJ*VIEW)
SO:
return glm::inverse(mProjection * mView) * ((screenPos - glm::vec4(mW / 2, mH / 2, 0, 0)) / glm::vec4(mW / 2, -mH / 2, 1, 1));

Draw a rounded rectangle using a single glDrawElement( triangle_strip...) call in OpenGL ES

I want to draw a rounded rectangle in Opengl es, with a single glDraw call.
I have also tried it. and shared it in the answer.
Hope it will be helpful.
I have tried to draw a rounded rectangle in opengl es with single glDraw call.
Below is the code snippet:
// -0.3f, -0.2f, 0.0f, // 0
// 0.3f, -0.2f, 0.0f, // 1
// -0.3f, 0.2f, 0.0f, // 2
// 0.3f, 0.2f, 0.0f, // 3
// 0.6f, 0.2f, 0.0f, // 4
// 0.6f, -0.2f, 0.0f, // 5
// 0.6f, -0.5f, 0.0f, // 6
// 0.3f, -0.5f, 0.0f, // 7
// -0.3f, -0.5f, 0.0f, // 8
// -0.6f, -0.5f, 0.0f, // 9
// -0.6f, -0.2f, 0.0f, // 10
// -0.6f, 0.2f, 0.0f, // 11
// -0.6f, 0.5f, 0.0f, // 12
// -0.3f, 0.5f, 0.0f, // 13
// 0.3f, 0.5f, 0.0f, // 14
// 0.6f, 0.5f, 0.0f // 15
//
// 8_______________________7
// /| |\
// 9/ | | \6
// 10/ | | \5
// /___|______________________|___\
// 1 | |2 3| |4
// | | | |
// | | | |
// 12 |___|______________________|___|19
// \ |0 1| /
// 13\ | | /18
// 14\ | | /17
// \|______________________|/
// 15 16
//
static GLfloat vertRndRect[N];
// first store the vertices 0,1,2,3,4 in vertRndRect array
// then calculate the value for vertices 5 and 6 using the below code
for (i = 30; i < 90; i = i + 30) // change this line to create more vertices but 'indices' array will change
{
float X_cen = vert1[9];
float Y_cen = vert1[10];
vertRndRect[iVertCnt++] = X_cen + (cos(degreesToRadians(i)) * rad);
vertRndRect[iVertCnt++] = Y_cen + (sin(degreesToRadians(i)) * rad);
vertRndRect[iVertCnt++] = 0.0f; // Z
}
// Then store vertices 7 and 8 to vertRndRect array
// then calculate the value for vertices 9 and 10 using the below code
for (i = 120; i < 180; i = i + 30)
{
float X_cen = vert1[6];
float Y_cen = vert1[7];
vertRndRect[iVertCnt++] = X_cen + (cos(degreesToRadians(i)) * rad);
vertRndRect[iVertCnt++] = Y_cen + (sin(degreesToRadians(i)) * rad);
vertRndRect[iVertCnt++] = 0.0f; // Z
}
// Then store vertices 11 and 12 to vertRndRect array
// then calculate the value for vertices 13 and 14 using the below code
for (i = 210; i < 270; i = i + 30)
{
float X_cen = vert1[0];
float Y_cen = vert1[1];
vertRndRect[iVertCnt++] = X_cen + (cos(degreesToRadians(i)) * rad);
vertRndRect[iVertCnt++] = Y_cen + (sin(degreesToRadians(i)) * rad);
vertRndRect[iVertCnt++] = 0.0f; // Z
}
// Then store vertices 15 and 16 to vertRndRect array
// then calculate the value for vertices 13 and 14 using the below code
for (i = 300; i < 360; i = i + 30)
{
float X_cen = vert1[3];
float Y_cen = vert1[4];
vertRndRect[iVertCnt++] = X_cen + (cos(degreesToRadians(i)) * rad);
vertRndRect[iVertCnt++] = Y_cen + (sin(degreesToRadians(i)) * rad);
vertRndRect[iVertCnt++] = 0.0f; // Z
}
// Then store vertices 19
//////////////////////////
GLushort indices[] = { 0, 1, 2, 3, 3, 4,
5, 5, 3, 6, 7, 7,
3, 8, 2, 9, 10, 10,
2, 11, 0, 12, 13, 13,
0, 14, 15, 15, 0, 16,
1, 17, 18, 18, 1, 19, 3, 4
};
//////////////////////////
glDrawElements(GL_TRIANGLE_STRIP, 38, GL_UNSIGNED_SHORT,indices); // 38 is size of 'indices' array
Here, I calculated only 2 vertices for each rounded corner.
But can increase it to get more smoothness.
But corresponding changes has to be done in 'indices' array
Lot of optimizations can be done in this code.
I modified the code to get the inputs ( TopLeft Corner Position, width and height) and dynamically generating 10 vertices for each rounded corners and its texture coordinates.
// Vertices and Texture Coordinates Generation.
Input: Width and Height, Sx, Sy,Sz (Top Left Vertex Position)
// To get smooth rounded rectangle, 10 vertices are generated for each rounded corner
float Sx = 0.0;
float Sy = 0.0;
float verCz = Sz = 0.0;
float rad;
if (fWidth > fHeight)
{
rad = 0.3f * (fHeight/fWidth);
}
else
{
rad = 0.3f * (fWidth/fHeight);
}
float invWidth = 1.0/fWidth ;
float invHeight = 1.0/fHeight;
float radbywidth = rad * invWidth;
float radbyheight = rad * invHeight;
float texCx = (Sx + fWidth - rad) * invWidth;
float texCy = (Sy - rad) * invHeight;
//0 to 9 vertices
for (i = 0; i <= 90; i = i + 10)
{
vertices[iVertCnt++] = (Sx + fWidth - rad) + (cos(degreesToRadians(i)) * rad); //centre point-X + r*cos
vertices[iVertCnt++] = (Sy - rad) + (sin(degreesToRadians(i)) * rad); //centre point-Y + r*sin
vertices[iVertCnt++] = verCz;
texcoord_RndRect [tex++] = texCx + (cos(degreesToRadians(i)) * radbywidth);
texcoord_RndRect [tex++] = texCy + (sin(degreesToRadians(i)) * radbyheight);
}
GLfloat vert1[] =
{
(Sx + rad), Sy , 0.0f, // 10
(Sx + rad), (Sy -rad), 0.0f, // 11
};
for (i = 0; i < 6; i = i+3)
{
vertices[iVertCnt++] = vert1[i];
vertices[iVertCnt++] = vert1[i+1];
vertices[iVertCnt++] = vert1[i+2];
texcoord_RndRect [tex++] = vert1[i] * invWidth;
texcoord_RndRect [tex++] = vert1[i+1] * invHeight;
}
texCx = (Sx + rad) * invWidth;
texCy = (Sy - rad) * invHeight;
////12 to 21 vertices
for (i = 90; i <= 180; i = i + 10)
{
vertices[iVertCnt++] = (Sx + rad) + (cos(degreesToRadians(i)) * rad); //centre point-X + r*cos
vertices[iVertCnt++] = (Sy - rad) + (sin(degreesToRadians(i)) * rad); //centre point-Y + r*sin
vertices[iVertCnt++] = verCz;
texcoord_RndRect [tex++] = texCx +(cos(degreesToRadians(i)) * radbywidth); // texture will be from 0 to 1 only
texcoord_RndRect [tex++] = texCy +(sin(degreesToRadians(i)) * radbyheight);
}
GLfloat vert2[] =
{
(Sx) , (Sy - fHeight + rad), 0.0f, // 22
(Sx + rad ), (Sy - fHeight + rad), 0.0f, // 23
};
for (i = 0; i < 6; i = i+3)
{
vertices[iVertCnt++] = vert2[i];
vertices[iVertCnt++] = vert2[i+1];
vertices[iVertCnt++] = vert2[i+2];
texcoord_RndRect [tex++] = vert2[i] * invWidth;
texcoord_RndRect [tex++] = vert2[i+1] * invHeight;
}
texCx = (Sx + rad ) * invWidth;
texCy = (Sy - fHeight + rad) * invHeight;
////24 to 33 vertices
for (i = 180; i <= 270; i = i + 10)
{
vertices[iVertCnt++] = (Sx + rad ) + (cos(degreesToRadians(i)) * rad); //centre point-X + r*cos
vertices[iVertCnt++] = (Sy - fHeight + rad) + (sin(degreesToRadians(i)) * rad); //centre point-Y + r*sin
vertices[iVertCnt++] = verCz;
texcoord_RndRect [tex++] = texCx +(cos(degreesToRadians(i)) * radbywidth);
texcoord_RndRect [tex++] = texCy +(sin(degreesToRadians(i)) * radbyheight);
}
GLfloat vert3[] =
{
(Sx + fWidth - rad), (Sy - fHeight) , 0.0f, // 34
(Sx + fWidth - rad), (Sy - fHeight + rad) , 0.0f, // 35
};
for (i = 0; i < 6; i = i+3)
{
vertices[iVertCnt++] = vert3[i];
vertices[iVertCnt++] = vert3[i+1];
vertices[iVertCnt++] = vert3[i+2];
texcoord_RndRect [tex++] = vert3[i] * invWidth;
texcoord_RndRect [tex++] = vert3[i+1] * invHeight;
}
//36th vertices
vertices[iVertCnt++] = (Sx + fWidth - rad);
vertices[iVertCnt++] = (Sy - fHeight + rad);
vertices[iVertCnt++] = 0.0f;
texcoord_RndRect [tex++] = (Sx + fWidth - rad) * invWidth; // 11
texcoord_RndRect [tex++] = (Sy - fHeight + rad)* invHeight;
texCx = (Sx + fWidth - rad) * invWidth;
texCy = (Sy - fHeight + rad) *invHeight ;
////37 to 46 to vertices
for (i = 270; i <= 360; i = i + 10)
{
vertices[iVertCnt++] = (Sx + fWidth - rad) + (cos(degreesToRadians(i)) * rad); //centre point-X + r*cos
vertices[iVertCnt++] = (Sy - fHeight + rad) + (sin(degreesToRadians(i)) * rad); //centre point-Y + r*sin
vertices[iVertCnt++] = 0.0f;
texcoord_RndRect [tex++] = texCx +(cos(degreesToRadians(i)) * radbywidth);
texcoord_RndRect [tex++] = texCy +(sin(degreesToRadians(i)) * radbyheight);
}
GLfloat vert4[] =
{
(Sx + fWidth ) , (Sy - rad), 0.0f, // 47
(Sx + fWidth - rad) , (Sy -rad ), 0.0f, // 48
};
for (i = 0; i < 6; i = i+3)
{
vertices[iVertCnt++] = vert4[i];
vertices[iVertCnt++] = vert4[i+1];
vertices[iVertCnt++] = vert4[i+2];
texcoord_RndRect [tex++] = vert4[i] * invWidth;
texcoord_RndRect [tex++] = vert4[i+1] * invHeight;
}
// Display
///////////////////////////////////////
GLushort indices_topright[] =
{
0,1,2,3,4,5,6,7,8,9,10,11
};
GLushort indices_topleft[] =
{
12,13,14,15,16,17,18,19,20,21,22,23
};
GLushort indices_bottomleft[] =
{
24,25,26,27,28,29,30,31,32,33,34,35
};
GLushort indices_bottomright[] =
{
36,37,38,39,40,41,42,43,44,45,46,47,48,11,23
};
glDrawElements(GL_TRIANGLE_FAN, (sizeof(indices_topright)/sizeof(indices_topright[0])), GL_UNSIGNED_SHORT, indices_topright);
glDrawElements(GL_TRIANGLE_FAN, (sizeof(indices_topleft)/sizeof(indices_topleft[0])), GL_UNSIGNED_SHORT, indices_topleft);
glDrawElements(GL_TRIANGLE_FAN, (sizeof(indices_bottomleft)/sizeof(indices_bottomleft[0])), GL_UNSIGNED_SHORT, indices_bottomleft);
glDrawElements(GL_TRIANGLE_FAN, (sizeof(indices_bottomright)/sizeof(indices_bottomright[0])), GL_UNSIGNED_SHORT, indices_bottomright);
////////////////////////
This code works for me.

Using std::vector as vertex/element lists with OpenGL

I want to draw a bunch quads.
Right now I have a problem; drawing works fine and is fast but I'm using std::vector as containers for my quads and they're really, really slow. Coming from XNA I figured I should create something like the spriteBatch so that I can just call DrawQuad() to add the given quad to a list and then finally call End() to draw every quad.
My current code generally prints something like this to the console:
DrawQuad(): 77
End(): 0
Over and over again.
Main.cpp (sf::Clock is the clock class in SFML)
sf::Clock time;
for (int y = 0; y < 100; y++)
for (int x = 0; x < 100; x++)
renderer.DrawQuad("A", Vector2<GLfloat>(-1.0f + x * 0.02f, -1.0f + y * 0.02f));
std::cout << "DrawQuad(): " << time.getElapsedTime().asMilliseconds() << std::endl;
Renderer.cpp:
void TextRenderer::DrawQuad(string text, Vector2<GLfloat> position)
{
//TOP LEFT
vertexBufferVector.push_back(position.X);
vertexBufferVector.push_back(position.Y);
//TOP RIGHT
vertexBufferVector.push_back(position.X + 0.02f);
vertexBufferVector.push_back(position.Y);
//BOTTOM RIGHT
vertexBufferVector.push_back(position.X + 0.02f);
vertexBufferVector.push_back(position.Y + 0.02f);
//BOTTOM LEFT
vertexBufferVector.push_back(position.X);
vertexBufferVector.push_back(position.Y + 0.02f);
int elementCount = elementBufferVector.size() / 6;
elementBufferVector.push_back(elementCount * 4);
elementBufferVector.push_back(elementCount * 4 + 1);
elementBufferVector.push_back(elementCount * 4 + 2);
elementBufferVector.push_back(elementCount * 4 + 2);
elementBufferVector.push_back(elementCount * 4 + 3);
elementBufferVector.push_back(elementCount * 4);
}
void TextRenderer::End()
{
sf::Clock time;
GLfloat* vertexArray = &vertexBufferVector[0];
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * vertexBufferVector.size(), vertexArray, GL_STATIC_DRAW);
GLint* elementArray = &elementBufferVector[0];
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLint) * elementBufferVector.size(), elementArray, GL_STATIC_DRAW);
glDrawElements(GL_TRIANGLES, elementBufferVector.size(), GL_UNSIGNED_INT, 0);
vertexBufferVector.clear();
elementBufferVector.clear();
std::cout << "End(): " << time.getElapsedTime().asMilliseconds() << std::endl;
}
How do people who know what they're doing solve this? 10000 quads really shouldn't be an issue.
After writing all this I also increased the looping from (100, 100) to (1000, 100) and now the drawing takes 4-5 ms, is that considered good? I'm thinking no...
Since this is dead I'll just answer my question and maybe it'll help someone.
Instead of using vectors I resorted to using arrays with a set size. This brought the total rendering time for 100000 quads (with textures as well) down to an average of ~3.260ms.
My .h looks like this now:
const int MAX_BUFFER_SIZE = 1000;
const int MAX_VERTEX_BUFFER_SIZE = MAX_BUFFER_SIZE * 16;
const int MAX_ELEMENT_BUFFER_SIZE = MAX_BUFFER_SIZE * 6;
...
GLint vertexBufferArrayInserts;
GLfloat vertexBufferArray[MAX_VERTEX_BUFFER_SIZE];
GLint elementBufferArray[MAX_ELEMENT_BUFFER_SIZE];
And the relevant parts of the .cpp file:
void TextRenderer::DrawQuad(Vector2<GLfloat> position)
{
if(vertexBufferArrayInserts == MAX_BUFFER_SIZE)
End();
//TOP LEFT
vertexBufferArray[vertexBufferArrayInserts * 16] = position.X;
vertexBufferArray[vertexBufferArrayInserts * 16 + 1] = position.Y;
vertexBufferArray[vertexBufferArrayInserts * 16 + 2] = 0.0f;
vertexBufferArray[vertexBufferArrayInserts * 16 + 3] = 0.0f;
//TOP RIGHT
vertexBufferArray[vertexBufferArrayInserts * 16 + 4] = position.X + 16.0f;
vertexBufferArray[vertexBufferArrayInserts * 16 + 5] = position.Y;
vertexBufferArray[vertexBufferArrayInserts * 16 + 6] = 24.0f / 512.0f;
vertexBufferArray[vertexBufferArrayInserts * 16 + 7] = 0.0f;
//BOTTOM RIGHT
vertexBufferArray[vertexBufferArrayInserts * 16 + 8] = position.X + 16.0f;
vertexBufferArray[vertexBufferArrayInserts * 16 + 9] = position.Y + 16.0f;
vertexBufferArray[vertexBufferArrayInserts * 16 + 10] = 24.0f / 512.0f;
vertexBufferArray[vertexBufferArrayInserts * 16 + 11] = 32.0f / 512.0f;
//BOTTOM LEFT
vertexBufferArray[vertexBufferArrayInserts * 16 + 12] = position.X;
vertexBufferArray[vertexBufferArrayInserts * 16 + 13] = position.Y + 16.0f;
vertexBufferArray[vertexBufferArrayInserts * 16 + 14] = 0.0f;
vertexBufferArray[vertexBufferArrayInserts * 16 + 15] = 32.0f / 512.0f;
//ELEMENT BUFFER
elementBufferArray[vertexBufferArrayInserts * 6] = vertexBufferArrayInserts * 4;
elementBufferArray[vertexBufferArrayInserts * 6 + 1] = vertexBufferArrayInserts * 4 + 1;
elementBufferArray[vertexBufferArrayInserts * 6 + 2] = vertexBufferArrayInserts * 4 + 2;
elementBufferArray[vertexBufferArrayInserts * 6 + 3] = vertexBufferArrayInserts * 4 + 2;
elementBufferArray[vertexBufferArrayInserts * 6 + 4] = vertexBufferArrayInserts * 4 + 3;
elementBufferArray[vertexBufferArrayInserts * 6 + 5] = vertexBufferArrayInserts * 4;
vertexBufferArrayInserts++;
}
void TextRenderer::End()
{
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * vertexBufferArrayInserts * 16, vertexBufferArray);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(GLint) * vertexBufferArrayInserts * 6, elementBufferArray);
glDrawElements(GL_TRIANGLES, vertexBufferArrayInserts * 6, GL_UNSIGNED_INT, 0);
vertexBufferArrayInserts = 0;
}
And this is how I timed it:
sf::Clock timer;
renderer.Begin(projMatrix);
for (int y = 0; y < 100; y++)
for(int x = 0; x < 1000; x++)
renderer.DrawQuad(Vector2<GLfloat>(16.0f * x, 16.0f * y));
renderer.End();
avgDrawTime += timer.getElapsedTime().asMicroseconds();
drawCalls++;
if(drawCalls % 100 == 0)
std::cout << avgDrawTime / drawCalls << std::endl;