OPENGL GLEW Can someone help me to set my cylinder horizontal - c++

Can someone help me set up my cylinder horizontal. My library is GLEW. If you need more detail about the code I can provide it.
void UCreateSpeaker(GLfloat verts[], GLushort indices[], int numSides, float radius, float halfLen) {
const float TWO_PI = 2.0f * 3.1415926f;
const float radiansPerSide = TWO_PI / numSides;
int currentVertex = 0;
verts[0] = 0.0f; //0 X
verts[1] = halfLen; //0 Y
verts[2] = 0.0f; //0 Z
verts[3] = 0.0f; //0 R
verts[4] = 0.0f; //0 G
verts[5] = 0.0f; //0 B
verts[6] = 1.0f; //0 A
currentVertex++;
verts[7] = 0.0f; //1 X
verts[8] = -halfLen; //1 Y
verts[9] = 0.0f; //1 Z
verts[10] = 0.0f; //1 R
verts[11] = 0.0f; //1 G
verts[12] = 0.0f; //1 B
verts[13] = 1.0f; //1 A
currentVertex++;
// Track of Triangles Added
int currentTriangle = 0;
for (int edge = 0; edge < numSides; edge++) {
// Calculate the Center Point to the Next Vertex
float theta = ((float)edge) * radiansPerSide;
verts[currentVertex * STRIDE + 0] = radius * cos(theta); // X
verts[currentVertex * STRIDE + 1] = halfLen; // Y
verts[currentVertex * STRIDE + 2] = radius * sin(theta); // Z
verts[currentVertex * STRIDE + 3] = 1.0f; // R
verts[currentVertex * STRIDE + 4] = 0.0f; // G
verts[currentVertex * STRIDE + 5] = 0.0f; // B
verts[currentVertex * STRIDE + 6] = 1.0f; // A
currentVertex++;
verts[currentVertex * STRIDE + 0] = radius * cos(theta); // X
verts[currentVertex * STRIDE + 1] = -halfLen; // Y
verts[currentVertex * STRIDE + 2] = radius * sin(theta); // Z
verts[currentVertex * STRIDE + 3] = 1.0f; // R
verts[currentVertex * STRIDE + 4] = 0.0f; // G
verts[currentVertex * STRIDE + 5] = 0.0f; // B
verts[currentVertex * STRIDE + 6] = 1.0f; // A
currentVertex++;
//Indices for the Triangles
if (edge > 0) {
// Top triangle
indices[(3 * currentTriangle) + 0] = 0; // Center-Top Prisim
indices[(3 * currentTriangle) + 1] = currentVertex - 4; // Upper-Left Side
indices[(3 * currentTriangle) + 2] = currentVertex - 2; // Upper Right Side
currentTriangle++;
// Bottom Triangle
indices[(3 * currentTriangle) + 0] = 1; // Center-Top Prisim
indices[(3 * currentTriangle) + 1] = currentVertex - 3; // Upper-Left Side
indices[(3 * currentTriangle) + 2] = currentVertex - 1; // Upper Right Side
currentTriangle++;
// Triangle for 1/2 Rectangular Side
indices[(3 * currentTriangle) + 0] = currentVertex - 4; // Upper-Left Side
indices[(3 * currentTriangle) + 1] = currentVertex - 3; // Bottom-Left Side
indices[(3 * currentTriangle) + 2] = currentVertex - 1; // Bottom-Right Side
currentTriangle++;
// Traingle for Second 1/2 rectangular Side
indices[(3 * currentTriangle) + 0] = currentVertex - 1; // Bottom-Right Side
indices[(3 * currentTriangle) + 1] = currentVertex - 2; // Upper-Right Side
indices[(3 * currentTriangle) + 2] = currentVertex - 4; // Upper-Left Side
currentTriangle++;
}
}
// Indices for Triangles
// Top Triangle
indices[(3 * currentTriangle) + 0] = 0; // Center Top Prims
indices[(3 * currentTriangle) + 1] = currentVertex - 2; // Upper-Left Side
indices[(3 * currentTriangle) + 2] = 2; // Fist Upper-Left (Change to Right)
currentTriangle++;
// Bottom Triangle
indices[(3 * currentTriangle) + 0] = 1; //Center-Bottom Prism
indices[(3 * currentTriangle) + 1] = currentVertex - 1; //Bottom-Left Side
indices[(3 * currentTriangle) + 2] = 3; // Fist Bottom-Left (Change to Right)
currentTriangle++;
// First Triangle 1/2 Rectangular Side
indices[(3 * currentTriangle) + 0] = currentVertex - 2; // Upper-Left Side
indices[(3 * currentTriangle) + 1] = currentVertex - 1; //Bottom-Left Side
indices[(3 * currentTriangle) + 2] = 3; // Bottom-Right Side
currentTriangle++;
// Second Triangle 1/2 Rectangular Side
indices[(3 * currentTriangle) + 0] = 3; // Bottom-Right Side
indices[(3 * currentTriangle) + 1] = 2; // Upper-Right Side
indices[(3 * currentTriangle) + 2] = currentVertex - 2; // Upper-Left Side
currentTriangle++;
}

Related

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 can I adjust my mouse coord to isometric tile coordinate to compensate for diamond shaped tiles?

My drawing function looks like this:
for(std::size_t y = 0; y < mapSizeY; ++y)
{
for(std::size_t x = mapSizeX-1; x != static_cast<std::size_t>(-1); --x)
{
auto tile = GetTile(x,y);
int64_t xPos = (x * TILE_WIDTH / 2) + (y * TILE_WIDTH / 2);
int64_t yPos = (y * TILE_HEIGHT / 2) - (x * TILE_HEIGHT / 2);
xPos += camera.GetXOffset();
yPos += camera.GetYOffset();
yPos -= tile->z * TILE_HEIGHT/2; // everything is at height 1 for now
auto zoom = camera.GetZoomFactor();
xPos *= zoom;
yPos *= zoom;
if(xPos < 0-TILE_WIDTH*zoom || yPos < 0-TILE_HEIGHT*zoom)
continue;
if(xPos > GetScreenDimensions().x || yPos > GetScreenDimensions().y)
continue;
// x is up right
// y is down right
Blit(m_grass,{xPos,yPos,TILE_WIDTH*zoom,TILE_HEIGHT*zoom+TILE_HEIGHT/2*zoom});
}
}
When a user clicks the mouse, I'm trying to get the x,y coord of the tile clicked on. To translate the mouse coords to the map coords I'm doing the following:
// x and y are the mouse coords
auto zoom = camera.GetZoomFactor();
auto xOffset = camera.GetXOffset();
auto yOffset = camera.GetYOffset();
int z = 1; // every tile is at height 1 for now
int32_t xTile = -(TILE_HEIGHT * (2 * xOffset * zoom + TILE_WIDTH * z * zoom - 2 * x) + 2 * TILE_WIDTH * (y - yOffset * zoom)) / (2 * TILE_HEIGHT * TILE_WIDTH * zoom);
int32_t yTile = (-2 * TILE_HEIGHT * xOffset * zoom + TILE_HEIGHT * TILE_WIDTH * z * zoom + 2 * TILE_HEIGHT * x - 2 * yOffset * TILE_WIDTH * zoom + 2 * TILE_WIDTH * y) / (2 * TILE_HEIGHT * TILE_WIDTH * zoom);
I came up with the above by solving my draw positions for x,y
The results I get appear to be on the correct tile. But the issue is that if I click past the "halfway" point of a tile, in either x or y coordinate, it attributes it to the next tile (because they're squares with transparent corners to make the "diamond" shape).
How can I adjust the above to compensate?

Cylinder drawn with triangles in OpenGL

It's drawing a cylinder with stacks and edges but the problem is that stacks are connected to one point instead of a new one.
Maybe a picture will show it better:
And here's how I'm rendering the side because disks are rendered separately:
for (int i = 1; i <= height; ++i) {
for (int j = 0; j < edges; ++j) {
glBegin(GL_TRIANGLES); {
// 0 bottom
glVertex3f(x + radius * cos(theta + interval), y , z + radius * sin(theta + interval));
// 1 bottom
glVertex3f(x + radius * cos(theta), y + y_value * i, z + radius * sin(theta));
// 2 top
glVertex3f(x + radius * cos(theta), y + y_value * i, z + radius * sin(theta));
// 2 top
glVertex3f(x + radius * cos(theta), y + y_value * i, z + radius * sin(theta));
// 3 top
glVertex3f(x + radius * cos(theta + interval), y + y_value * i, z + radius * sin(theta + interval));
// 0 bottom
glVertex3f(x + radius * cos(theta + interval), y , z + radius * sin(theta + interval));
} glEnd();
theta += interval;
}
theta = 0.0;
}
I've been trying to solve it for days and I run out of ideas. Do you know what I am doing wrong?
UPDATE:
I've changed it to be render with quads using ybungalobill advice. Now I'm struggling with UV mapping. And hopefully once this part is solved it'll be easy enough to convert into triangles.
That's what I have now:
And that's the code I'm using for UV mapping:
u = 0.0,
v = 0.0,
u_inter = 1.0 / edges,
v_inter = 1.0 / y_value; // (y_value = height / edges)
for (int i = 1; i <= height; ++i) {
for (int j = 0; j < edges; ++j) {
glBegin(GL_QUAD_STRIP); {
// 0 bottom
glTexCoord2f(u, v);
// 1 bottom
glTexCoord2f(u + u_inter, v);
// 2 top
glTexCoord2f(u + u_inter, v + v_inter);
// 3 top
glTexCoord2f(u, v + v_inter);
} glEnd();
theta += interval;
u += u_inter;
}
v += v_inter;
theta = 0.0;
}
float y0 = y + y_value * (i-1);
float y1 = y + y_value * i;
// 0 bottom
glVertex3f(x + radius * cos(theta + interval), y0, z + radius * sin(theta + interval));
// 1 bottom
glVertex3f(x + radius * cos(theta), y0, z + radius * sin(theta));
// 2 top
glVertex3f(x + radius * cos(theta), y1, z + radius * sin(theta));
// 2 top
glVertex3f(x + radius * cos(theta), y1, z + radius * sin(theta));
// 3 top
glVertex3f(x + radius * cos(theta + interval), y1, z + radius * sin(theta + interval));
// 0 bottom
glVertex3f(x + radius * cos(theta + interval), y0, z + radius * sin(theta + interval));

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;

how to extrude a path in 3d?

I'm trying to extrude a path in 3d. Nothing fancy yet, just following some points and using a regular polygon for 'tubing'. I'm using Processing for now to quickly prototype, but will later turn the code into OpenGL.
My problem is rotating the 'joints' at the right angles. I think I have a rough idea how to get the angles, not sure.
I've started from a sample by Simon Greenwold(Processing > File > Examples > 3D > Form > Vertices).Here's my attempt so far:
UPDATE > REFACTORED/SIMPLIFIED CODE
Here is the main sketch code:
int pointsNum = 10;
Extrusion star;
int zoom = 0;
void setup() {
size(500, 500, P3D);
PVector[] points = new PVector[pointsNum+1];
for(int i = 0 ; i <= pointsNum ; i++){
float angle = TWO_PI/pointsNum * i;
if(i % 2 == 0)
points[i] = new PVector(cos(angle) * 100,sin(angle) * 100,0);
else
points[i] = new PVector(cos(angle) * 50,sin(angle) * 50,0);
}
star = new Extrusion(10,10,points,3);
}
void draw() {
background(0);
lights();
translate(width / 2, height / 2,zoom);
rotateY(map(mouseX, 0, width, 0, PI));
rotateX(map(mouseY, 0, height, 0, PI));
rotateZ(-HALF_PI);
noStroke();
fill(255, 255, 255);
translate(0, -40, 0);
star.draw();
}
void keyPressed(){
if(key == 'a') zoom += 5;
if(key == 's') zoom -= 5;
}
And here is the Extrusion class:
import processing.core.PMatrix3D;
class Extrusion{
float topRadius,bottomRadius,tall,sides;
int pointsNum;
PVector[] points;
Extrusion(){}
Extrusion(float topRadius, float bottomRadius, PVector[] points, int sides) {
this.topRadius = topRadius;
this.bottomRadius = bottomRadius;
this.points = points;
this.pointsNum = points.length;
this.sides = sides;
}
void draw() {
if(pointsNum >= 2){
float angle = 0;
float angleIncrement = TWO_PI / sides;
//begin draw segments between caps
angle = 0;
for(int i = 1; i < pointsNum ; ++i){
beginShape(QUAD_STRIP);
for(int j = 0; j < sides + 1; j++){
vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius);
vertex(points[i].x + cos(angle) * bottomRadius, points[i].y, points[i].z + sin(angle) * bottomRadius);
angle += angleIncrement;
}
endShape();
}
//begin draw segments between caps
}else println("Not enough points: " + pointsNum);
}
}
UPDATE
Here is how my sketch looks like:
processing extrude http://doc.gold.ac.uk/~ma802gp/extrude.gif
The problem is the joints aren't at the right angle, so the extrude looks wrong.
This isn't a very good example, as this could be achieved with a lathe. If I can get a lathe to work with an arbitrary set of points and an axis that will be great. I am using extrusion
because I am trying to create geometric bodies based on the art of Liviu Stoicoviciu.
Here are some samples:
star painting http://doc.gold.ac.uk/~ma802gp/star_painting.jpg
star paper sculpture http://doc.gold.ac.uk/~ma802gp/star_paper_sculpture.jpg
triangles http://doc.gold.ac.uk/~ma802gp/triangles_pencil.jpg
Sorry about the poor quality.
As you can see in the triangles image, that would be achieved with extrusions.
UPDATE
Here's my attempt to use drhirsch's help in the draw method:
void draw() {
if(pointsNum >= 2){
float angle = 0;
float angleIncrement = TWO_PI / sides;
//begin draw segments between caps
angle = 0;
for(int i = 1; i < pointsNum ; ++i){
beginShape(QUAD_STRIP);
for(int j = 0; j < sides + 1; j++){
PVector s = new PVector(0,0,1);
PVector cn = new PVector();
points[i].normalize(cn);
PVector r = s.cross(cn);
float a = acos(s.dot(cn));
PMatrix3D rot = new PMatrix3D(1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1);
rot.rotate(a,r.x,r.y,r.z);
PVector rotVec = new PVector();
rot.mult(points[i],rotVec);
rotVec.add(new PVector(cos(angle) * topRadius,0,sin(angle) * topRadius));
vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius);
vertex(rotVec.x,rotVec.y,rotVec.y);
//vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius);
//vertex(points[i].x + cos(angle) * bottomRadius, points[i].y, points[i].z + sin(angle) * bottomRadius);
angle += angleIncrement;
}
endShape();
}
//begin draw segments between caps
}else println("Not enough points: " + pointsNum);
}
I've refactored the code so now the class that used to be called CShape is called Extrude, the code is less and hopefully simples, and I use an array of PVector objects instead of a Vector of PVector objects which might be confusing.
Here is my yet another attempt with some escher-esque results:
upated draw
void draw() {
if(pointsNum >= 2){
float angle = 0;
float angleIncrement = TWO_PI / sides;
//begin draw segments between caps
angle = 0;
for(int i = 1; i < pointsNum ; ++i){
beginShape(QUAD_STRIP);
float angleBetweenNextAndPrevious = 0.0;
if(i < pointsNum - 1) angleBetweenNextAndPrevious = PVector.angleBetween(points[i],points[i+1]);
for(int j = 0; j < sides + 1; j++){
PVector s = new PVector(0,0,1);
PVector s2 = new PVector(0,0,1);
PVector cn = new PVector();
PVector cn2 = new PVector();
points[i-1].normalize(cn);
points[i].normalize(cn);
PVector r = s.cross(cn);
PVector r2 = s.cross(cn2);
PMatrix3D rot = new PMatrix3D(1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1);
PMatrix3D rot2 = new PMatrix3D(1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1);
rot.rotate(angleBetweenNextAndPrevious,r.x,r.y,r.z);
rot2.rotate(angleBetweenNextAndPrevious,r2.x,r2.y,r2.z);
PVector rotVec = new PVector();
rot.mult(points[i-1],rotVec);
rotVec.add(new PVector(cos(angle) * topRadius,0,sin(angle) * topRadius));
PVector rotVec2 = new PVector();
rot2.mult(points[i],rotVec2);
rotVec2.add(new PVector(cos(angle) * topRadius,0,sin(angle) * topRadius));
vertex(rotVec.x,rotVec.y,rotVec.z);
vertex(rotVec2.x,rotVec2.y,rotVec2.z);
//vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius);
//vertex(points[i].x + cos(angle) * bottomRadius, points[i].y, points[i].z + sin(angle) * bottomRadius);
angle += angleIncrement;
}
endShape();
}
//begin draw segments between caps
}else println("Not enough points: " + pointsNum);
}
}
fix_test http://doc.gold.ac.uk/~ma802gp/extrude2.gif
Edit by drhirsch
This should work:
void draw() {
if(pointsNum >= 2){
float angle = 0;
float angleIncrement = TWO_PI / sides;
//begin draw segments between caps
angle = 0;
for(int i = 1; i < pointsNum ; ++i){
beginShape(QUAD_STRIP);
float angleBetweenNextAndPrevious = 0.0;
if(i < pointsNum - 1) angleBetweenNextAndPrevious = PVector.angleBetween(points[i],points[i+1]);
PVector s = new PVector(0,0,1);
PVector s2 = new PVector(0,0,1);
PVector cn = new PVector();
PVector cn2 = new PVector();
points[i-1].normalize(cn);
points[i].normalize(cn2);
PVector r = s.cross(cn);
PVector r2 = s.cross(cn2);
PMatrix3D rot = new PMatrix3D(1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1);
PMatrix3D rot2 = new PMatrix3D(1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1);
rot.rotate(angleBetweenNextAndPrevious,r.x,r.y,r.z);
rot2.rotate(angleBetweenNextAndPrevious,r2.x,r2.y,r2.z);
PVector rotVec = new PVector();
PVector rotVec2 = new PVector();
for(int j = 0; j < sides + 1; j++){
// I am still not sure about this. Should the shape be in the xy plane
// if the extrusion is mainly along the z axis? If the shape is now in
// the xz plane, you need to use (0,1,0) as normal vector of the shape
// (this would be s and s2 above, don't use the short names I have
// used, sorry)
PVector shape = new PVector(cos(angle) * topRadius,0,sin(angle) * topRadius);
rot.mult(shape, rotVec);
rot2.mult(shape,rotVec2);
rotVec.add(points[i-1]);
rotVec2.add(points[i]);
vertex(rotVec.x,rotVec.y,rotVec.z);
vertex(rotVec2.x,rotVec2.y,rotVec2.z);
//vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius);
//vertex(points[i].x + cos(angle) * bottomRadius, points[i].y, points[i].z + sin(angle) * bottomRadius);
angle += angleIncrement;
}
endShape();
}
//begin draw segments between caps
}else println("Not enough points: " + pointsNum);
}
}
UPDATE
Here is a simple illustration of my problem:
description http://doc.gold.ac.uk/~ma802gp/description.gif
The blue path is equivalent to the points[] PVector array in my code, if pointsNum = 6.
The red path is what I'm struggling to solve, the green path is what I want to achieve.
UPDATE
Some minor issues with the order of vertices I think.
Here are some print screens using 6 points and no (if/else % 2) star condition.
points1 http://doc.gold.ac.uk/~ma802gp/points1.gif
alt text http://doc.gold.ac.uk/~ma802gp/points2.gif
Assuming your shape has a normal vector S. In your example S would be (0,0,1), because your shape is flat in xy. You can use the cross product between the current path vector V (normalized) and S to obtain the rotation axis vector R. You need to rotate your shape around R. The angle of rotation can be obtained from the dot product between S and V. So:
R = S x V
a = arc cos(S . V)
Now you can setup a rotation matrix with R and a and rotate the shape by it.
You can use glRotate(...) to rotate the current matrix on the stack, but this can't be done between glBegin() and glEnd(). So you have to do the matrix multiplication by yourself or with a library.
Edit: After a short look at the library you are using, you should be able to setup the rotation matrix with
PVector s = new PVector(0,0,1); // is already normalized (meaning is has length 1)
PVector cn;
current.normalize(cn);
PVector r = s.cross(cn);
float a = acos(s.dot(cn));
PMatrix rot = new PMatrix(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
rot.rotate(a, r.x, r.y, r.z);
and now multiply each element of your shape with rot and translate it by your current path vector:
PVector rotVec;
rot.mult((PVector)shape[i], rotVec);
rotVec.add(current);