I have to do my homework (draw a butterfly in OpenGL) for university. Currently I'm working on the body of the butterfly, which is a simple ellipse (it will be a 2D game). I have to draw this filled ellipse with triangles, because we mustn't use other libraries (I assume there's a simple Circle() or something like that function).
Now I'm able to draw a triangle, but creating a Triangle from the butterfly's body's class don't work. Here is the code:
class Triangle {
unsigned int vao;
unsigned int vbo[2];
float phi;
float vertexCoords[6];
float vertexColors[9] = {1,0,0, 0,1,0, 0,0,1};
public:
Triangle(float x1,float y1,float x2,float y2,float x3, float y3) {
vertexCoords[0] = x1;
vertexCoords[1] = y1;
vertexCoords[2] = x2;
vertexCoords[3] = y2;
vertexCoords[4] = x3;
vertexCoords[5] = y3;
Animate(0);
}
void setTriangleColor(float r, float g, float b) {
for (int i = 0; i < 9; i+=3) {
vertexColors[i] = r;
vertexColors[i + 1] = g;
vertexColors[i + 2] = b;
}
Create();
}
void setPointColor(float r, float g, float b, int count) {
vertexColors[count * 3] = r;
vertexColors[count * 3 + 1] = g;
vertexColors[count * 3 + 2] = b;
Create();
}
void Create() {
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(2, &vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER,sizeof(vertexCoords),vertexCoords,GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0,2, GL_FLOAT,GL_FALSE,0, NULL);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexColors), vertexColors, GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
}
void Animate(float t) { /*phi = t;*/ phi = 0; }
void Draw() {
mat4 ScaleSmaller(
0.01, 0 , 0 , 0,
0 , 0.01 , 0 , 0,
0 , 0 , 0.01 , 0,
0 , 0 , 0 , 1
);
mat4 MVPTransform1(1, 0, 0, 0,
0, cosf(phi), -sinf(phi), 0,
0, sinf(phi), cosf(phi), 0,
0, 0, 0, 1);
int location = glGetUniformLocation(shaderProgram, "MVP");
if (location >= 0) glUniformMatrix4fv(location, 1, GL_TRUE, (MVPTransform1*ScaleSmaller));
else printf("uniform MVP cannot be set\n");
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
};
struct Point {
float x, y;
Point(float x,float y):x(x),y(y){}
Point():x(0),y(0){}
};
class MyEllipse {
float width;
float height;
Point origo;
std::vector<Triangle> triangles = std::vector<Triangle>();
public:
MyEllipse() {}
MyEllipse(Point origo,float width, float height):origo(origo),width(width),height(height) {
float px = origo.x + (width / 2) * cosf(90);
float py = origo.y + (height / 2) * cosf(90);
float prevX = origo.x + (width / 2) * cosf(0);
float prevY = origo.y + (height / 2) * cosf(0);
Triangle elem1(origo.x, origo.y, px, py, prevX, prevY);
triangles.push_back(elem1);
}
void Create() {
for (Triangle value : triangles) {
value.Create();
}
}
void Draw() {
for (Triangle value : triangles) {
value.Draw();
}
}
};
In the code I create a Triangle and a MyEllipse with the same parameters in the same location.
float px = 10 + (50 / 2) * cosf(90);
float py = 10 + (50 / 2) * cosf(90);
float prevX = 10 + (50 / 2) * cosf(0);
float prevY = 10 + (50 / 2) * cosf(0);
Triangle triangle(10, 0, px, py, prevX, prevY);
MyEllipse test1 = MyEllipse(Point(10, 0), 50, 50);
(Later I'll add more triangles to the vector, now I'M just testing.) The problem, that I can see the triangle, but in case that I delete the triangle create line, I don't see anything. I didn't forget to add the appropriate Draw() and Create() function calls, in the other part of the code, when I call triangle.Create() or triangle.Draw(), immediately after that I call the test1.Create() and test1.Draw() functions.
Can anybody tell me anything about this issue, because I'm afraid I have no idea to solve the issue.
In the methodes MyEllipse::Create
and MyEllipse ::Draw
a copy of each triangle is created when the triangles are iterated:
for (Triangle value : triangles) // <- the "content" of "triangles" is copied to "value"
{
value.Create();
}
You have to use a reference to the triangles in the container
for (Triangle &value : triangles) {
.....
}
Further cos
, cosf
, cosl
computes the cosine of arg, measured in radians.
This means you have to use
#define _USE_MATH_DEFINES
#include <math.h>
float angle_in_degree = ....;
cosf(angle_in_degree * M_PI/180.0f);
If you can't see the triangles, then this may be the case, because 2 points of the triangle are almost identical. Change the polygon mode, to draw lines instead of polygons (for debug reason only):
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
glDrawArrays(GL_TRIANGLES, 0, 3);
But creating separate vertex buffers and vertex array objects for each triangle is a very bad idea. Create one vertex buffer and one vertex array object for the mesh.
I recommend to create the vertex buffer and the vertex array object somehow like this:
float a = 0.2f;
float b = 0.5f;
int no_of_triangles = 20;
std::vector<float> varray{ 0.0f, 0.0f, 0.5f, 0.5f, 0.5f };
for ( int i = 0; i <= no_of_triangles; ++i )
{
float angle = (float)i/(float)no_of_triangles * 2.0f * M_PI;
float x = cos( angle );
float y = sin( angle );
varray.push_back( x * a );
varray.push_back( y * b );
varray.push_back( 0.5f - x * 0.5f );
varray.push_back( 0.5f - y * 0.5f );
varray.push_back( (0.5f*x+0.5f)*(0.5f*y+0.5f) );
}
GLuint vbo;
glGenBuffers( 1, &vbo );
glBindBuffer( GL_ARRAY_BUFFER, vbo );
glBufferData( GL_ARRAY_BUFFER, varray.size()*sizeof(*varray.data()), varray.data(), GL_STATIC_DRAW );
GLuint vao;
glGenVertexArrays( 1, &vao );
glBindVertexArray( vao );
glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 5*sizeof(*varray.data()), 0 );
glEnableVertexAttribArray( 0 );
glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, 5*sizeof(*varray.data()), (void*)(2*sizeof(*varray.data())) );
glEnableVertexAttribArray( 1 );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glBindVertexArray( 0 );
Since the vertex buffer contains the center point and a list of the points around the ellipse, the mesh can be drawn by the primitive type GL_TRIANGLE_FAN
:
glBindVertexArray( vao );
glDrawArrays( GL_TRIANGLE_FAN, 0, (GLsizei)varray.size()/5 );
glBindVertexArray( 0 );
Preview: