Related
I am using glBufferSubData to change positions of vertices.
Everything works fine, when I call the code below once.
If I run this whole code two times to add two pointclouds the vertex positions is updated only for the last geometry.
Please look at only following methods of the class, because the rest of the code is just a constructor to initialize the clouds:
void opengl_init()
void opengl_draw()
I think something is wrong with winding to the vao buffer.
Question:
How can I correctly update vertex positions glBufferSubData so that both pointclouds would move?
class pointcloud {
public:
int id = 0;
std::vector<vertex> vertices;
std::vector<unsigned int> indices;//pairs
unsigned int vao, vbo, ibo;
//individual polylines
pointcloud(const std::vector<float>& coord, const std::vector<float>& colors)//, std::vector<Texture> textures
{
vertices.reserve(coord.size());
indices.reserve((coord.size() - 1) * 2);
for (int i = 0; i < coord.size(); i += 3) {
vertex vertex;
vertex.position = glm::vec3(coord[i + 0], coord[i + 1], coord[i + 2]);
vertex.color = glm::vec3(colors[i + 0], colors[i + 1], colors[i + 2]);
vertices.emplace_back(vertex);
}
for (int i = 0; i < (coord.size() / 3) - 1; i++) {
indices.emplace_back(i + 0);
indices.emplace_back(i + 1);
}
// now that we have all the required data, set the vertex buffers and its attribute pointers.
//setup_polyline();
}
//merged polylines
pointcloud(const std::vector<std::vector<float>>& coord, const std::vector<std::vector<float>>& colors)//, std::vector<Texture> textures
{
//reserve memory
int v_count = 0;
int i_count = 0;
for (int i = 0; i < coord.size(); i++) {
v_count += coord[i].size();
i_count += coord[i].size() - 1;
}
vertices.reserve(v_count);
indices.reserve(i_count);
//fill vertics and indices lists
for (int i = 0; i < coord.size(); i++) {
for (int j = 0; j < coord[i].size(); j += 3) {
vertex vertex;
vertex.position = glm::vec3(coord[i][j + 0], coord[i][j + 1], coord[i][j + 2]);
vertex.color = glm::vec3(colors[i][j + 0], colors[i][j + 1], colors[i][j + 2]);
vertices.emplace_back(vertex);
}
}
v_count = 0;
for (int i = 0; i < coord.size(); i++) {
for (int j = 0; j < (coord[i].size() / 3) - 1; j++) {
//std::cout << v_count + j + 0 << " " << v_count + j + 1 << std::endl;
indices.emplace_back(v_count + j + 0);
indices.emplace_back(v_count + j + 1);
}
v_count += (coord[i].size() / 3);
}
//std::cout << vertices.size() << std::endl;
// now that we have all the required data, set the vertex buffers and its attribute pointers.
//setup_polyline();
}
//// initializes all the buffer objects/arrays
void opengl_init(bool draw_dynamic = true, int _id = 0)
{
id = _id + 1;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// init vertex-array and vertex-array-buffer
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//vertex array
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
//bind vertex-array-buffer to the vertex-array
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//declare array with data or empty array depending how data will be displayed
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
auto type = !draw_dynamic ? GL_STATIC_DRAW : GL_STREAM_DRAW;
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vertex), &vertices[0], type); // target | size | data (poinnting to first element e.g. glm::value_ptr(vertices[0])) | usage
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// set attributes that corresponds to layout id in the vertex shader
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// vertex Positions
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (void*)0);
// vertex normals
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (void*)offsetof(vertex, color));
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//bind buffers vao | vbo | ibo
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//glBindVertexArray(0);
}
// render the mesh
void opengl_draw(opengl_shaders::shader& shader, bool draw_dynamic = true)
{
//update
//https://learnopengl.com/Advanced-OpenGL/Advanced-Data
if (draw_dynamic) {
for (auto& v : vertices)
v.position.y += 0.001;
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(vertex), &vertices[0]);
}
//draw
glBindVertexArray(vao);
glDrawArrays(GL_POINTS, 0, vertices.size());
//glBindVertexArray(0);
}
void opengl_clear(opengl_shaders::shader& shader) {
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
shader.delete_shader();
}
};
glBufferSubData updates a subset of the data store of a buffer object that is currently bound to the specified target. You must bind the buffer before you can modify its data:
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(vertex), &vertices[0]);
The GL_ARRAY_BUFFER binding is a global state. This binding is maintained until it is changed. As for your code it works for 1 pointcloud, but it doesn't work if you have more than 1 pointcloud and vertex buffer.
i am trying to build a tessellated rectangle based from the answer to this question.
How to build data for a tessellated rectangle
My final result has only the half rectangle drawn and the geometry looks broken.
This is how it looks.
This is my code which i have tried to port to VAO and VBO.
Generating the Data.
std::vector<float> verticesRect;
std::vector<unsigned int> indicesRect;
nSegments = 16; mSegments = 16;
void Rectangle2::Generate()
{
const int n8 = nSegments * 8; // size of VBO gfx data
const int sz0 = mSegments * n8; // size of VBO gfx data
const int sz1 = (mSegments - 1) * (nSegments - 1) * 6;// size of indices
verticesRect.clear();
indicesRect.clear();
int a,i, j, k, b;
GLfloat x, y, z, dx, dy, l;
glm::vec3 u, v, nor;
dx = 2.0 * (width / float(nSegments - 1));
dy = 2.0 * (height / float(mSegments - 1));
for (a = 0,y = -height, j = 0; j < mSegments; j++, y += dy)
for (x = -width, i = 0; i < nSegments; i++, x += dx)
{
z = 20.0 * sin((x * x) + (y * y));
verticesRect.push_back(x); a++;
verticesRect.push_back(y); a++;
verticesRect.push_back(z); a++;
// Normal ( will be recomputed later)
verticesRect.push_back(0.0); a++;
verticesRect.push_back(0.0); a++;
verticesRect.push_back(1.0); a++;
// TexCoord
verticesRect.push_back((x + width) / (width + width)); a++;
verticesRect.push_back((y + height) / (height + height)); a++;
}
// triangulation indices
for(a = 0, j = 1; j < mSegments; j++ )
for (i = 1; i < nSegments; i++)
{
b = ((nSegments * j) + i) * 8;
// First triangle per quad
indicesRect.push_back(b - 8); a++;
indicesRect.push_back(b - 8 - n8); a++;
indicesRect.push_back(b); a++;
// Second triangle per quad
indicesRect.push_back(b - 8 - n8); a++;
indicesRect.push_back(b - n8); a++;
indicesRect.push_back(b); a++;
// recompute inner normals
for (k = 0; k < 3; k++) {
u[k] = verticesRect[indicesRect[a - 6] + k] - verticesRect[indicesRect[a - 4] + k];
v[k] = verticesRect[indicesRect[a - 5] + k] - verticesRect[indicesRect[a - 4] + k];
}
glm::vec3 cross1 = crossProduct(u, v);
cross1 = glm::normalize(cross1);
for (k = 0; k < 3; k++) {
u[k] = verticesRect[indicesRect[a - 3] + k] - verticesRect[indicesRect[a - 1] + k];
v[k] = verticesRect[indicesRect[a - 2] + k] - verticesRect[indicesRect[a - 1] + k];
}
glm::vec3 cross2 = crossProduct(u, v);
cross2 = glm::normalize(cross2);
for (k = 0; k < 3; k++) {
verticesRect[indicesRect[a - 1] + 3 + k] = 0.5 * (cross1[k] + cross2[k]);
}
}
}
Creating the VAO and VBO
void Rectangle2::init()
{
Generate();
glGenVertexArrays(1, &m_VAO);
glGenBuffers(1, &m_VBO);
glGenBuffers(1, &m_EBO);
glBindVertexArray(m_VAO);
glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
glBufferData(GL_ARRAY_BUFFER, verticesRect.size() * sizeof(float), &verticesRect[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * indicesRect.size(), &indicesRect[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
isInited = true;
}
Drawing the Object.
glBindVertexArray(m_VAO);
glDrawElements(GL_TRIANGLES, indicesRect.size() - 1, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
The problem is with indices... You ported my old code which uses direct indices but you use VBO instead which uses logical indices ... so the remedy is just divide all indices by 8 (stride size) at the end of Generate function (or reindex the whole genere to use logical indices instead but thats more coding ...) Here full C++/VCL working code to cross reference:
//---------------------------------------------------------------------------
#include <vcl.h> // VCL stuff (ignore)
#include <math.h>
#pragma hdrstop // VCL stuff (ignore)
#include "Unit1.h" // VCL stuff (header of this window)
#include "gl_simple.h" // my GL init (source included)
//---------------------------------------------------------------------------
#pragma package(smart_init) // VCL stuff (ignore)
#pragma resource "*.dfm" // VCL stuff (ignore)
TForm1 *Form1; // VCL stuff (this window)
//---------------------------------------------------------------------------
const int m=16,n=16; // points per grid axis
const int n8=n*8; // size of VBO gfx data
const int sz0=m*n8; // size of VBO gfx data
const int sz1=(m-1)*(n-1)*6;// size of indices
GLfloat dat[sz0];
GLuint idx[sz1];
//---------------------------------------------------------------------------
GLfloat divide(GLfloat a,GLfloat b){ if (fabs(b)<1e-10) return 0.0; else return a/b; }
void normalize(GLfloat *c,GLfloat *a) // c = a/|a|
{
GLfloat l=divide(1.0,sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])));
c[0]=a[0]*l;
c[1]=a[1]*l;
c[2]=a[2]*l;
}
void cross(GLfloat *c,GLfloat *a,GLfloat *b) // c = cross(a,b)
{
GLfloat q[3];
q[0]=(a[1]*b[2])-(a[2]*b[1]);
q[1]=(a[2]*b[0])-(a[0]*b[2]);
q[2]=(a[0]*b[1])-(a[1]*b[0]);
for(int i=0;i<3;i++) c[i]=q[i];
}
void genere(GLfloat w,GLfloat h)
{
int i,j,k,a,b;
GLfloat x,y,z,dx,dy,l;
GLfloat u[3],v[3],nor[3];
// gfx data
dx=2.0*w/GLfloat(n-1);
dy=2.0*h/GLfloat(m-1);
for (a=0,y=-h,j=0;j<m;j++,y+=dy)
for ( x=-w,i=0;i<n;i++,x+=dx)
{
// Vertex
// z= 0.3*sin((x*x)+(y*y));
z=20.0*sin((x*x)+(y*y));
dat[a]=x; a++;
dat[a]=y; a++;
dat[a]=z; a++;
// Normal (will be recomputed latter)
dat[a]=0.0; a++;
dat[a]=0.0; a++;
dat[a]=1.0; a++;
// TexCoord
dat[a]=(x+w)/(w+w); a++;
dat[a]=(y+h)/(h+h); a++;
}
// triangulation indices
for (a=0,j=1;j<m;j++)
for ( i=1;i<n;i++)
{
// b = location of point[i,j] in dat[]
b=((n*j)+i)*8;
// first triangle per quad
idx[a]=b-8; a++;
idx[a]=b-8-n8; a++;
idx[a]=b; a++;
// second triangle per quad
idx[a]=b-8-n8; a++;
idx[a]=b-n8; a++;
idx[a]=b; a++;
// recompute inner normals
for (k=0;k<3;k++)
{
u[k]=dat[idx[a-6]+k]-dat[idx[a-4]+k];
v[k]=dat[idx[a-5]+k]-dat[idx[a-4]+k];
}
cross(nor,u,v); normalize(nor,nor);
for (k=0;k<3;k++)
{
u[k]=dat[idx[a-3]+k]-dat[idx[a-1]+k];
v[k]=dat[idx[a-2]+k]-dat[idx[a-1]+k];
}
cross(u,u,v); normalize(u,u);
for (k=0;k<3;k++) dat[idx[a-1]+3+k]=0.5*(nor[k]+u[k]);
}
// copy edge normals
for (j=0,i=1;i<n;i++)
{
// b = location of point[i,j] in dat[]
b=((n*j)+i)*8;
// copy
for (k=0;k<3;k++) dat[b+3+k]=dat[b+3+k+n8];
}
for (i=0,j=1;j<m;j++)
{
// b = location of point[i,j] in dat[]
b=((n*j)+i)*8;
// copy
for (k=0;k<3;k++) dat[b+3+k]=dat[b+3+k+8];
}
for (i=0;i<sz1;i++) idx[i]/=8; // !!! this is what you need to add !!!
}
//---------------------------------------------------------------------------
GLuint m_VAO=0,m_VBO=0,m_EBO=0;
void genere_VBO()
{
GLuint i;
glGenVertexArrays(1,&m_VAO);
glBindVertexArray(m_VAO);
glGenBuffers(1,&m_VBO);
glBindBuffer(GL_ARRAY_BUFFER,m_VBO);
glBufferData(GL_ARRAY_BUFFER,sizeof(dat),dat,GL_STATIC_DRAW);
i=0; glEnableVertexAttribArray(i); // vertex
glVertexAttribPointer(i,3,GL_FLOAT,GL_FALSE,8*sizeof(dat[0]),(void*)(0*sizeof(dat[0])));
i=2; glEnableVertexAttribArray(i); // normal
glVertexAttribPointer(i,3,GL_FLOAT,GL_FALSE,8*sizeof(dat[0]),(void*)(3*sizeof(dat[0])));
i=8; glEnableVertexAttribArray(i); // texcoord0
glVertexAttribPointer(i,2,GL_FLOAT,GL_FALSE,8*sizeof(dat[0]),(void*)(6*sizeof(dat[0])));
glGenBuffers(1,&m_EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(idx),idx,GL_STATIC_DRAW);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(8);
}
//---------------------------------------------------------------------------
void gl_draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
float light[4]={-0.3,-0.7,0.0,0.0};
glLightfv(GL_LIGHT0,GL_POSITION,light);
glTranslatef(0.0,+6.0,-30.0);
glRotatef(135.0,1.0,0.0,0.0);
static float ang=0;
glRotatef(ang,0.0,0.0,1.0); ang=fmod(ang+1.5,360.0);
/*
// old api render (just for debug ignore this)
int i,j;
glColor3f(0.1,0.5,0.7);
glBegin(GL_TRIANGLES);
for (i=0;i<sz1;i++)
{
j=idx[i]*8; // !!! I added *8 as indices are logical now)
glNormal3fv(dat+j+3);
glTexCoord3fv(dat+j+6);
glVertex3fv(dat+j);
}
glEnd();
*/
// new api render
glColor3f(0.1,0.5,0.7);
glBindVertexArray(m_VAO);
glDrawElements(GL_TRIANGLES,sizeof(idx)/sizeof(idx[0]),GL_UNSIGNED_INT,0); // indices (choose just one line not both !!!)
glBindVertexArray(0);
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
// this is called on window startup
gl_init(Handle); // init OpenGL 1.0
genere(1.0,1.0);
genere_VBO();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// this is called before window exits
gl_exit(); // exit OpenGL
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
// this is called on each window resize (and also after startup)
gl_resize(ClientWidth,ClientHeight);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
// this is called whnewer app needs repaint
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::tim_redrawTimer(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
And preview:
using fixed function and nVidia Default attribute locations (was too lazy to make shaders for this)...
Im working on my 2D tile map sidescroller game in c++, Im using few libraries like openGL, openAL etc.
Right now im reworking my render part of my game to make it better from performance side, before i started working on it, I had working version, but it was calling something like 180isch draw calls every frame, which is bad.
So i decided to go with instanced rendering for each texture in my game (if I use 90 textures on my map, i will try to render 90 draw calls, but if texture is not on my screen i will skip it), when i got to it and tried to render something i got cornered, I did some calculations and everything should be fine but it isnt.
if (game->data->currentWorld != 0)
{
tileSizeX = (2.0f / tileXsideCount);
tileSizeY = (2.0f / tileYsideCount);
float worldXoffset = getWorldOffsetX();
float worldYoffset = getWorldOffsetY();
float playerXoffset = getPlayerOffsetX();
float playerYoffset = getPlayerOffsetY();
int tileXoffset = getTileOffsetX();
int tileYoffset = getTileOffsetY();
int* textRenderCount = new int[game->data->currentWorld->textureCount];
float vertices[] =
{
-1 + tileSizeX, -1 + tileSizeY , 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right
-1 + tileSizeX, -1, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // bottom right
-1, -1, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom left
-1, -1 + tileSizeY, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f // top left
};
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
for (int i = 1; i < game->data->currentWorld->textureCount; i++)
{
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
int offsetIndex = 0;
unsigned int texture;
if (game->data->loadedTextures[i] != NULL)
{
texture = game->data->loadedTextures[i];
}
else
{
std::cout << "Renderer::RenderLoop - texture not found for id: " << i << endl;
}
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// texture coord attribute
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
tempShader->use();
glBindTexture(GL_TEXTURE_2D, texture);
for(int x = 0; x < game->data->currentWorld->sizeX; x++)
{
for (int y = 0; y < game->data->currentWorld->sizeY; y++)
{
if (
x > (tileXoffset - (tileXsideCount / 2) - 1) &&
x < (tileXoffset + (tileXsideCount)+1) &&
y >(tileYoffset - (tileYsideCount / 2) - 1) &&
y < (tileYoffset + (tileYsideCount)+1)
)
{
if (game->data->currentWorld->world[x][y]->id == i)
{
if (game->data->debugTiles)
{
std::cout << "For tile: x: " << x << " y: " << y;
}
float offsetX = { ((x*1.0f) - worldXoffset)*tileSizeX};
float offsetY = { ((y*1.0f) - worldYoffset)*tileSizeY};
if (game->data->debugTiles)
{
std::cout << " offset is: " << offsetX << offsetY << endl;
std::cout << "worldXoffset is : " << worldXoffset << endl;
std::cout << "worldYoffset is : " << worldYoffset << endl;
}
stringstream ss;
std::string indexText;
ss << offsetIndex;
indexText = ss.str();
tempShader->setVec2(("offset[" + indexText + "]").c_str(), offsetX, offsetY);
offsetIndex++;
}
}
}
}
if (game->data->debugTiles)
{
std::cout << "RenderCount for last render request:" << offsetIndex+1 <<endl;
game->data->debugTiles = false;
}
glBindVertexArray(VAO);
glDrawArraysInstanced(GL_TRIANGLES,0, 6,offsetIndex+1);
UnloadSceneBuffers();
}
and here my functions to return offsets:
float Renderer::getWorldOffsetX()
{
if (game->data->currentChar->xPos < tileXsideCount / 2)
{
return 0.0f;
}
else if(game->data->currentChar->xPos > game->data->currentWorld->sizeX - (tileXsideCount/2))
{
return (game->data->currentWorld->sizeX - (tileXsideCount / 2));
}
else
{
return (game->data->currentChar->xPos - tileXsideCount/2);
}
};
float Renderer::getWorldOffsetY()
{
if (game->data->currentChar->yPos < tileYsideCount / 2)
{
return 0.0f;
}
else if (game->data->currentChar->yPos > game->data->currentWorld->sizeY - (tileYsideCount / 2))
{
return (game->data->currentWorld->sizeY - (tileYsideCount / 2));
}
else
{
return (game->data->currentChar->yPos - tileYsideCount/2);
}
};
float Renderer::getPlayerOffsetX()
{
if (game->data->currentChar->xPos < tileXsideCount / 2)
{
return -1.0f + (game->data->currentChar->xPos*tileSizeX);
}
else if (game->data->currentChar->xPos > game->data->currentWorld->sizeX - (tileXsideCount / 2))
{
return +1.0f - (tileSizeX*((game->data->currentWorld->sizeX-tileXsideCount) - game->data->currentChar->xPos));
}
else
{
return 0.0f;
}
};
float Renderer::getPlayerOffsetY()
{
if (game->data->currentChar->yPos < tileYsideCount / 2)
{
return -1.0f + (game->data->currentChar->yPos*tileSizeY);
}
else if (game->data->currentChar->yPos > game->data->currentWorld->sizeY - (tileYsideCount / 2))
{
return +1.0f - (tileSizeY*((game->data->currentWorld->sizeY - tileYsideCount) - game->data->currentChar->yPos));
}
else
{
return 0.0f;
}
};
int Renderer::getTileOffsetX()
{
if (game->data->currentChar->xPos < tileXsideCount / 2)
{
return 0 + tileXsideCount/2;
}
else if (game->data->currentChar->xPos > game->data->currentWorld->sizeX - (tileXsideCount / 2))
{
return (game->data->currentWorld->sizeX-(tileXsideCount/2));
}
else
{
return game->data->currentChar->xPos;
}
}
int Renderer::getTileOffsetY()
{
if (game->data->currentChar->yPos < tileYsideCount / 2)
{
return 0 + tileYsideCount / 2;
}
else if (game->data->currentChar->yPos > game->data->currentWorld->sizeY -(tileYsideCount / 2))
{
return (game->data->currentWorld->sizeY - (tileYsideCount / 2));
}
else
{
return game->data->currentChar->yPos;
}
}
and here are my shaders:
Vertex:
#version 330 core
layout(location = 0) in vec2 aPos;
layout(location = 1) in vec3 aColor;
layout(location = 2) in vec2 aTexCoord;
out vec2 TexCoord;
uniform vec2 offset[198];
void main()
{
vec2 currentOffset = offset[gl_InstanceID];
gl_Position = vec4(aPos + currentOffset,0.0f,1.0f);
TexCoord = aTexCoord;
}
Fragment:
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
void main()
{
FragColor = texture(ourTexture, TexCoord);
}
and this is my shader set vec2 function (its mostly the same as on learnopengl.com):
void setVec2(const std::string &name, float input1,float input2)
{
glUniform2f(glGetUniformLocation(ID, name.c_str()), input1,input2);
}
Edit:
This is what i see:
In order to use modern openGl with tinyobjloader, I'm trying to change the viewer exemple.
I just change the LoadObjAndConvert function, to add vertex array objects as i seen in this tutorial, and to no longer use the buffer object that contains all the data (position, indices, color, uv) because it seems that we can no longer use it with modern openGL.
Result look like I have bad vertex index, the model is only partly rendered, and if the model has only one mesh (the stanford bunny) it does not even show up.
The code is too long, but it is the same as the tinyobjloader viewer exemple, so I will only post functions that are different.
Here is the LoadObjAndConvert function modified (modified parts are between lines to help) :
static bool LoadObjAndConvert(float bmin[3], float bmax[3],
std::vector<DrawObject>* drawObjects,
std::vector<tinyobj::material_t>& materials,
std::map<std::string, GLuint>& textures,
const char* filename) {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
timerutil tm;
tm.start();
std::string base_dir = GetBaseDir(filename);
if (base_dir.empty()) {
base_dir = ".";
}
#ifdef _WIN32
base_dir += "\\";
#else
base_dir += "/";
#endif
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename,
base_dir.c_str());
if (!err.empty()) {
std::cerr << err << std::endl;
}
tm.end();
if (!ret) {
std::cerr << "Failed to load " << filename << std::endl;
return false;
}
printf("Parsing time: %d [ms]\n", (int)tm.msec());
printf("# of vertices = %d\n", (int)(attrib.vertices.size()) / 3);
printf("# of normals = %d\n", (int)(attrib.normals.size()) / 3);
printf("# of texcoords = %d\n", (int)(attrib.texcoords.size()) / 2);
printf("# of materials = %d\n", (int)materials.size());
printf("# of shapes = %d\n", (int)shapes.size());
// Append `default` material
materials.push_back(tinyobj::material_t());
for (size_t i = 0; i < materials.size(); i++) {
printf("material[%d].diffuse_texname = %s\n", int(i),
materials[i].diffuse_texname.c_str());
}
// Load diffuse textures
{
for (size_t m = 0; m < materials.size(); m++) {
tinyobj::material_t* mp = &materials[m];
if (mp->diffuse_texname.length() > 0) {
// Only load the texture if it is not already loaded
if (textures.find(mp->diffuse_texname) == textures.end()) {
GLuint texture_id;
int w, h;
int comp;
std::string texture_filename = mp->diffuse_texname;
if (!FileExists(texture_filename)) {
// Append base dir.
texture_filename = base_dir + mp->diffuse_texname;
if (!FileExists(texture_filename)) {
std::cerr << "Unable to find file: " << mp->diffuse_texname
<< std::endl;
exit(1);
}
}
unsigned char* image =
stbi_load(texture_filename.c_str(), &w, &h, &comp, STBI_default);
if (!image) {
std::cerr << "Unable to load texture: " << texture_filename
<< std::endl;
exit(1);
}
std::cout << "Loaded texture: " << texture_filename << ", w = " << w
<< ", h = " << h << ", comp = " << comp << std::endl;
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (comp == 3) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
GL_UNSIGNED_BYTE, image);
} else if (comp == 4) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
GL_UNSIGNED_BYTE, image);
} else {
assert(0); // TODO
}
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(image);
textures.insert(std::make_pair(mp->diffuse_texname, texture_id));
}
}
}
}
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
{
for (size_t s = 0; s < shapes.size(); s++) {
/*-----------------------------------------------------------*/
DrawObject o;// I keep this object for later purpose, texture, etc
//std::vector<float> buffer; // pos(3float), normal(3float), color(3float)
//I replace "buffer" by arrays:
std::vector<GLfloat> mesh_vertex;
std::vector<GLfloat> mesh_normals;
std::vector<GLfloat> mesh_colors;
std::vector<GLfloat> mesh_textCoords;
std::vector<GLuint> mesh_indices;
/*fill index array*/
for (long i = 0; i < shapes[s].mesh.indices.size(); i++)
{
mesh_indices.push_back(shapes[s].mesh.indices[i].vertex_index);
}
/*-----------------------------------------------------------*/
// Check for smoothing group and compute smoothing normals
std::map<int, vec3> smoothVertexNormals;
if (hasSmoothingGroup(shapes[s]) > 0) {
std::cout << "Compute smoothingNormal for shape [" << s << "]" << std::endl;
computeSmoothingNormals(attrib, shapes[s], smoothVertexNormals);
}
for (size_t f = 0; f < shapes[s].mesh.indices.size() / 3; f++) {
tinyobj::index_t idx0 = shapes[s].mesh.indices[3 * f + 0];
tinyobj::index_t idx1 = shapes[s].mesh.indices[3 * f + 1];
tinyobj::index_t idx2 = shapes[s].mesh.indices[3 * f + 2];
int current_material_id = shapes[s].mesh.material_ids[f];
if ((current_material_id < 0) ||
(current_material_id >= static_cast<int>(materials.size()))) {
// Invaid material ID. Use default material.
current_material_id =
materials.size() -
1; // Default material is added to the last item in `materials`.
}
// if (current_material_id >= materials.size()) {
// std::cerr << "Invalid material index: " << current_material_id <<
// std::endl;
//}
//
float diffuse[3];
for (size_t i = 0; i < 3; i++) {
diffuse[i] = materials[current_material_id].diffuse[i];
}
float tc[3][2];
if (attrib.texcoords.size() > 0) {
if ((idx0.texcoord_index < 0) || (idx1.texcoord_index < 0) ||
(idx2.texcoord_index < 0)) {
// face does not contain valid uv index.
tc[0][0] = 0.0f;
tc[0][1] = 0.0f;
tc[1][0] = 0.0f;
tc[1][1] = 0.0f;
tc[2][0] = 0.0f;
tc[2][1] = 0.0f;
} else {
assert(attrib.texcoords.size() >
size_t(2 * idx0.texcoord_index + 1));
assert(attrib.texcoords.size() >
size_t(2 * idx1.texcoord_index + 1));
assert(attrib.texcoords.size() >
size_t(2 * idx2.texcoord_index + 1));
// Flip Y coord.
tc[0][0] = attrib.texcoords[2 * idx0.texcoord_index];
tc[0][1] = 1.0f - attrib.texcoords[2 * idx0.texcoord_index + 1];
tc[1][0] = attrib.texcoords[2 * idx1.texcoord_index];
tc[1][1] = 1.0f - attrib.texcoords[2 * idx1.texcoord_index + 1];
tc[2][0] = attrib.texcoords[2 * idx2.texcoord_index];
tc[2][1] = 1.0f - attrib.texcoords[2 * idx2.texcoord_index + 1];
}
} else {
tc[0][0] = 0.0f;
tc[0][1] = 0.0f;
tc[1][0] = 0.0f;
tc[1][1] = 0.0f;
tc[2][0] = 0.0f;
tc[2][1] = 0.0f;
}
float v[3][3];
for (int k = 0; k < 3; k++) {
int f0 = idx0.vertex_index;
int f1 = idx1.vertex_index;
int f2 = idx2.vertex_index;
assert(f0 >= 0);
assert(f1 >= 0);
assert(f2 >= 0);
v[0][k] = attrib.vertices[3 * f0 + k];
v[1][k] = attrib.vertices[3 * f1 + k];
v[2][k] = attrib.vertices[3 * f2 + k];
bmin[k] = std::min(v[0][k], bmin[k]);
bmin[k] = std::min(v[1][k], bmin[k]);
bmin[k] = std::min(v[2][k], bmin[k]);
bmax[k] = std::max(v[0][k], bmax[k]);
bmax[k] = std::max(v[1][k], bmax[k]);
bmax[k] = std::max(v[2][k], bmax[k]);
}
float n[3][3];
{
bool invalid_normal_index = false;
if (attrib.normals.size() > 0) {
int nf0 = idx0.normal_index;
int nf1 = idx1.normal_index;
int nf2 = idx2.normal_index;
if ((nf0 < 0) || (nf1 < 0) || (nf2 < 0)) {
// normal index is missing from this face.
invalid_normal_index = true;
} else {
for (int k = 0; k < 3; k++) {
assert(size_t(3 * nf0 + k) < attrib.normals.size());
assert(size_t(3 * nf1 + k) < attrib.normals.size());
assert(size_t(3 * nf2 + k) < attrib.normals.size());
n[0][k] = attrib.normals[3 * nf0 + k];
n[1][k] = attrib.normals[3 * nf1 + k];
n[2][k] = attrib.normals[3 * nf2 + k];
}
}
} else {
invalid_normal_index = true;
}
if (invalid_normal_index && !smoothVertexNormals.empty()) {
// Use smoothing normals
int f0 = idx0.vertex_index;
int f1 = idx1.vertex_index;
int f2 = idx2.vertex_index;
if (f0 >= 0 && f1 >= 0 && f2 >= 0) {
n[0][0] = smoothVertexNormals[f0].v[0];
n[0][1] = smoothVertexNormals[f0].v[1];
n[0][2] = smoothVertexNormals[f0].v[2];
n[1][0] = smoothVertexNormals[f1].v[0];
n[1][1] = smoothVertexNormals[f1].v[1];
n[1][2] = smoothVertexNormals[f1].v[2];
n[2][0] = smoothVertexNormals[f2].v[0];
n[2][1] = smoothVertexNormals[f2].v[1];
n[2][2] = smoothVertexNormals[f2].v[2];
invalid_normal_index = false;
}
}
if (invalid_normal_index) {
// compute geometric normal
CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0];
n[1][1] = n[0][1];
n[1][2] = n[0][2];
n[2][0] = n[0][0];
n[2][1] = n[0][1];
n[2][2] = n[0][2];
}
}
for (int k = 0; k < 3; k++) {
/*-----------------------------------------------------------*/
// I leave old calls to "buffer" in comment for understanding
//buffer.push_back(v[k][0]);
//buffer.push_back(v[k][1]);
//buffer.push_back(v[k][2]);
mesh_vertex.push_back(v[k][0]);
mesh_vertex.push_back(v[k][1]);
mesh_vertex.push_back(v[k][2]);
//buffer.push_back(n[k][0]);
//buffer.push_back(n[k][1]);
//buffer.push_back(n[k][2]);
mesh_normals.push_back(n[k][0]);
mesh_normals.push_back(n[k][1]);
mesh_normals.push_back(n[k][2]);
// Combine normal and diffuse to get color.
float normal_factor = 0.2;
float diffuse_factor = 1 - normal_factor;
float c[3] = {n[k][0] * normal_factor + diffuse[0] * diffuse_factor,
n[k][1] * normal_factor + diffuse[1] * diffuse_factor,
n[k][2] * normal_factor + diffuse[2] * diffuse_factor};
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 0.0f) {
float len = sqrtf(len2);
c[0] /= len;
c[1] /= len;
c[2] /= len;
}
//buffer.push_back(c[0] * 0.5 + 0.5);
//buffer.push_back(c[1] * 0.5 + 0.5);
//buffer.push_back(c[2] * 0.5 + 0.5);
mesh_colors.push_back(c[0] * 0.5 + 0.5);
mesh_colors.push_back(c[1] * 0.5 + 0.5);
mesh_colors.push_back(c[2] * 0.5 + 0.5);
//buffer.push_back(tc[k][0]);
//buffer.push_back(tc[k][1]);
mesh_textCoords.push_back(tc[k][0]);
mesh_textCoords.push_back(tc[k][1]);
/*-----------------------------------------------------------*/
}
}
o.vb_id = 0;
o.numTriangles = 0;
// OpenGL viewer does not support texturing with per-face material.
if (shapes[s].mesh.material_ids.size() > 0 &&
shapes[s].mesh.material_ids.size() > s) {
o.material_id = shapes[s].mesh.material_ids[0]; // use the material ID
// of the first face.
} else {
o.material_id = materials.size() - 1; // = ID for default material.
}
printf("shape[%d] material_id %d\n", int(s), int(o.material_id));
/*-----------------------------------------------------------*/
/*if (buffer.size() > 0) {
glGenBuffers(1, &o.vb_id);
glBindBuffer(GL_ARRAY_BUFFER, o.vb_id);
glBufferData(GL_ARRAY_BUFFER, buffer.size() * sizeof(float),
&buffer.at(0), GL_STATIC_DRAW);
o.numTriangles = buffer.size() / (3 + 3 + 3 + 2) /
3; // 3:vtx, 3:normal, 3:col, 2:texcoord
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s),
o.numTriangles);
}
drawObjects->push_back(o);*/
// Replace by :
GLuint positionVBO = 0;
GLuint texcoordVBO = 0;
GLuint normalVBO = 0;
GLuint indicesEBO = 0;
// Upload per-vertex positions
if (!mesh_vertex.empty())
{
glGenBuffers(1, &positionVBO);
glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
glBufferData(GL_ARRAY_BUFFER, mesh_vertex.size() * sizeof(GLfloat), &mesh_vertex[0], GL_STATIC_DRAW); // GL_DYNAMIC_DRAW ?
glBindBuffer(GL_ARRAY_BUFFER, 0);
positionVBO_array.push_back(positionVBO);
}
// Upload per-vertex texture coordinates
if (!mesh_textCoords.empty())
{
glGenBuffers(1, &texcoordVBO);
glBindBuffer(GL_ARRAY_BUFFER, texcoordVBO);
glBufferData(GL_ARRAY_BUFFER,
mesh_textCoords.size() * sizeof(float),
&mesh_textCoords[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// Upload per-vertex normals
if (!mesh_normals.empty())
{
glGenBuffers(1, &normalVBO);
glBindBuffer(GL_ARRAY_BUFFER, normalVBO);
glBufferData(GL_ARRAY_BUFFER, mesh_normals.size() * sizeof(GLfloat), &mesh_normals[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
normalVBO_array.push_back(normalVBO);
}
// Upload the indices that form triangles
if (!shapes[0].mesh.indices.empty())
{
glGenBuffers(1, &indicesEBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
shapes[s].mesh.indices.size() * sizeof(unsigned int),
shapes[s].mesh.indices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
indicesEBO_array.push_back(indicesEBO);
indicesEBOSize_array.push_back(shapes[s].mesh.indices.size());
}
// Hook up vertex/index buffers to a "vertex array object" (VAO)
// VAOs are the closest thing OpenGL has to a "mesh" object.
// VAOs feed data from buffers to the inputs of a vertex shader.
GLuint meshVAO;
vglGenVertexArrays(1, &meshVAO);
meshVAO_array.push_back(meshVAO);// I keep the ids in order to loop inside meshVAO_array in the draw function
// Attach position buffer as attribute 0
if (positionVBO != 0)
{
glBindVertexArray(meshVAO);
// Note: glVertexAttribPointer sets the current
// GL_ARRAY_BUFFER_BINDING as the source of data
// for this attribute.
// That's why we bind a GL_ARRAY_BUFFER before
// calling glVertexAttribPointer then
// unbind right after (to clean things up).
glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
sizeof(float) * 3, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Enable the attribute (they are disabled by default
// -- this is very easy to forget!!)
glEnableVertexAttribArray(0);
glBindVertexArray(0);
}
// Attach texcoord buffer as attribute 1
if (texcoordVBO != 0)
{
glBindVertexArray(meshVAO);
glBindBuffer(GL_ARRAY_BUFFER, texcoordVBO);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,
sizeof(float) * 2, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(1);
glBindVertexArray(0);
}
// Attach normal buffer as attribute 2
if (normalVBO != 0)
{
glBindVertexArray(meshVAO);
glBindBuffer(GL_ARRAY_BUFFER, normalVBO);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE,
sizeof(float) * 3, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(2);
glBindVertexArray(0);
}
if (indicesEBO != 0)
{
glBindVertexArray(meshVAO);
// Note: Calling glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
// when a VAO is bound attaches the index buffer to the VAO.
// From an API design perspective, this is subtle.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesEBO);
glBindVertexArray(0);
}
/*-----------------------------------------------------------*/
}
}
printf("bmin = %f, %f, %f\n", bmin[0], bmin[1], bmin[2]);
printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]);
return true;
}
(Sorry for this long code block)
And here is the while loop of the main function, the only difference with tinyobjloader is between the two lines:
unsigned int program = shaders::CreateShader("data/simple.vert", "data/simple.frag"); // just some really simples shaders
while (glfwWindowShouldClose(window) == GL_FALSE) {
glfwPollEvents();
glClearColor(0.1f, 0.2f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
// camera & rotate
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
GLfloat mat[4][4];
gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0],
up[1], up[2]);
build_rotmatrix(mat, curr_quat);
glMultMatrixf(&mat[0][0]);
// Fit to -1, 1
glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent);
// Centerize object.
glTranslatef(-0.5 * (bmax[0] + bmin[0]), -0.5 * (bmax[1] + bmin[1]),
-0.5 * (bmax[2] + bmin[2]));
/*-----------------------------------------------------------*/
//Draw(gDrawObjects, materials, textures);
// Can now bind the vertex array object to
// the graphics pipeline, to render with it.
glUseProgram(program);
for (int s = 0; s < meshVAO_array.size(); s++)
{
glBindVertexArray(meshVAO_array[s]);
glDrawElements(GL_TRIANGLES, indicesEBOSize_array[s], GL_UNSIGNED_INT, 0);//mesh.IndexCount
glBindVertexArray(0);
}
glUseProgram(0);
// when done, unbind it from the graphics pipeline:
glBindVertexArray(0);
/*-----------------------------------------------------------*/
glfwSwapBuffers(window);
}
What am I doing wrong?
In the nested loops you all the indices of shapes[].mesh.indices are use to lokkup the attributes, which are stored in attrib.vertices, attrib.normals and attrib.texcoords.
This attributes are prepared and linearized. They are stored in there idexed order to the linear arrays mesh_vertex, mesh_normals, mesh_colors and mesh_textCoords.
But the indices are directly copied from shapes[].mesh.indices to mesh_indices
for (long i = 0; i < shapes[s].mesh.indices.size(); i++)
{
mesh_indices.push_back(shapes[s].mesh.indices[i].vertex_index);
}
The indices in mesh_indices still refer to the vertex coordinates stored in attrib.vertices but the have no meaning for the attributes in the new containers.
The original indices are not needed any more. The indices of the new attribute would be continuously ascending: [0, 1, 2, 3, 4, 5 ...]
It is sufficient to draw the array of generic vertex attribute data in its existing order:
// you have to know the number of attributes
// something like mesh_vertex.size() / 3;
GLsizei no_of_attributes = .... ;
glBindVertexArray(meshVAO_array[s]);
glDrawArrays(GL_TRIANGLES, 0, no_of_attributes);
glBindVertexArray(0);
I am using freetype and freetype-gl to render text.
Unfortunately my text is rendering as quads.
That's how I am doing it:
enum
{
//Max num of objects
MAX_SPRITES = 60000,
//Max num of indices
//One sprite got 6 indices
MAX_INDICES = MAX_SPRITES * 6,
//Once sprite got 4 vertices
SPRITE_SIZE = sizeof(Vertex) * 4,
//Max num of GL_TEXTUREX => X -> int <0; 32>
MAX_TEXTURES = 32
};
enum BUFFER_SIZE
{
VERTEX = SPRITE_SIZE * MAX_SPRITES,
INDEX = MAX_INDICES * sizeof(GLuint),
};
enum SHADER_OUT
{
POSITION = 0,
COLOR = 1,
UV = 2,
TEXTURE = 3,
};
class Renderer2D
{
public:
Renderer2D() = default;
~Renderer2D();
void Create();
void RenderClear();
void DrawString(const std::string& text, const Vector2& position, const Color& color);
void Render();
private:
Uint vao = 0,
vbo = 0,
ebo = 0,
indexCount = 0,
drawCalls = 0;
ftgl::texture_atlas_t* textureAtlas = nullptr;
ftgl::texture_font_t* font = nullptr;
std::vector<MiUint> textureSlots;
Vertex* mappedVertex = nullptr;
void End();
Uint* SetIndices();
float FindTexture();
};
Renderer2D::~Renderer2D()
{
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ebo);
glDeleteVertexArrays(1, &vao);
ftgl::texture_atlas_delete(textureAtlas);
ftgl::texture_font_delete(font);
textureSlots.clear();
}
void Renderer2D::Create()
{
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, BUFFER_SIZE::VERTEX, nullptr, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(SHADER_OUT::POSITION);
glEnableVertexAttribArray(SHADER_OUT::COLOR);
glEnableVertexAttribArray(SHADER_OUT::UV);
glEnableVertexAttribArray(SHADER_OUT::TEXTURE);
glVertexAttribPointer(SHADER_OUT::POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, position));
glVertexAttribPointer(SHADER_OUT::COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (const void*)offsetof(Vertex, color));
glVertexAttribPointer(SHADER_OUT::UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, uv));
glVertexAttribPointer(SHADER_OUT::TEXTURE, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, texID));
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, BUFFER_SIZE::INDEX, nullptr, GL_STATIC_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, NULL, BUFFER_SIZE::INDEX, SetIndices());
glBindVertexArray(0);
textureAtlas = ftgl::texture_atlas_new(512, 512, 1);
font = ftgl::texture_font_new_from_file(textureAtlas, 50, "Media/arial.ttf");
}
void Renderer2D::RenderClear()
{
textureSlots.clear();
mappedVertex = nullptr;
mappedVertex = (Vertex*)glMapNamedBufferRange(vbo, NULL, BUFFER_SIZE::VERTEX, GL_MAP_WRITE_BIT);
}
void Renderer2D::DrawString(const std::string& text, const Vector2& position, const Color& color)
{
using namespace ftgl;
float ts = FindTexture(),
x = position.x;
for (MiUint i = 0; i < text.length(); i++) {
char c = text[i];
texture_glyph_t* glyph = texture_font_get_glyph(font, c);
if (glyph != nullptr) {
if (i > 0) {
float kerning = texture_glyph_get_kerning(glyph, text[i - 1]);
x += kerning;
}
float x0 = x + glyph->offset_x,
x1 = x0 + glyph->width,
y0 = position.y + glyph->offset_y,
y1 = y0 - glyph->height,
u0 = glyph->s0,
u1 = glyph->s1,
v0 = glyph->t0,
v1 = glyph->t1;
mappedVertex->position = Vector3(x0, y0, 0.0f);
mappedVertex->uv = Vector2(u0, v0);
mappedVertex->texID = ts;
mappedVertex->color = color;
mappedVertex++;
mappedVertex->position = Vector3(x0, y1, 0.0f);
mappedVertex->uv = Vector2(u0, v1);
mappedVertex->texID = ts;
mappedVertex->color = color;
mappedVertex++;
mappedVertex->position = Vector3(x1, y1, 0.0f);
mappedVertex->uv = Vector2(u1, v1);
mappedVertex->texID = ts;
mappedVertex->color = color;
mappedVertex++;
mappedVertex->position = Vector3(x1, y0, 0.0f);
mappedVertex->uv = Vector2(u1, v0);
mappedVertex->texID = ts;
mappedVertex->color = color;
mappedVertex++;
indexCount += 6;
x += glyph->advance_x;
}
}
}
void Renderer2D::Render()
{
End();
drawCalls = 0;
for (int i = 0; i < (int)textureSlots.size(); ++i) {
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, textureSlots[i]);
}
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
++drawCalls;
indexCount = 0;
textureSlots.clear();
mappedVertex = nullptr;
glDisable(GL_BLEND);
}
void Renderer2D::End()
{
glUnmapNamedBuffer(vbo);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
MiUint* Renderer2D::SetIndices()
{
MiUint* indices = new MiUint[BUFFER_SIZE::INDEX],
offset = 0;
for (int i = 0; i < BUFFER_SIZE::INDEX; i += 6) {
indices[i] = offset;
indices[i + 1] = offset + 1;
indices[i + 2] = offset + 2;
indices[i + 3] = offset + 2;
indices[i + 4] = offset + 3;
indices[i + 5] = offset;
offset += 4;
}
return indices;
}
float Renderer2D::FindTexture()
{
float result = 0.0f;
bool found = false;
for (MiUint i = 0; i < (MiUint)textureSlots.size(); i++) {
if (textureSlots[i] == textureAtlas->id) {
result = (float)(i + 1);
found = true;
break;
}
}
if (!found) {
if (textureSlots.size() >= MAX_TEXTURES)
{
Render();
RenderClear();
}
textureSlots.push_back(textureAtlas->id);
result = (float)(textureSlots.size());
}
return result;
}
and my main is just setting up a 2d camera (ortho matrix, etc.), renderer.Create, in main loop clear renderer, drawString("STRING", Vector2(0.0f, 0.0f), Color(255, 0, 0, 255)) and renderer render.
Result is like:
How to fix it? I went through drawString method and it seems as if it works perfectly fine. What causes that it's rendered like this?
#Rabbid76
Simply just
Vertex:
#version 460 core
layout (location = 0) in vec2 a_position;
layout (location = 1) in vec4 a_color;
layout (location = 2) in vec2 a_uv;
layout (location = 3) in float a_texID;
out vec2 v_fragmentUV;
out vec4 v_fragmentColor;
out vec2 v_fragmenPosition;
out float v_texID;
uniform mat4 u_camera;
void main()
{
gl_Position = u_camera * vec4(a_position, 0.0, 1.0);
v_fragmenPosition = a_position;
v_fragmentColor = a_color;
v_texID = a_texID;
v_fragmentUV = vec2(a_uv.x, 1 - a_uv.y);
}
Pixel:
#version 460 core
out vec4 outColor;
in vec2 v_fragmentUV;
in vec4 v_fragmentColor;
in vec2 v_fragmenPosition;
in float v_texID;
layout (location = 0) uniform sampler2D u_textureSampler[32];
void main()
{
int texID = int(v_texID - 0.5);
outColor = texture(u_textureSampler[texID], v_fragmentUV);
}
I set tetxure sampler uniform in main. When I render textures like a player it's rendered very well. There are only text problems.
Wher do you use glUniform* to set the texture unit index to the texture sampler uniform?
Maybe I should just show my main class
App2D::App2D()
{
window.Init(4, 6);
window.Create("w", 1280, 720, WINDOW_RESIZABLE);
camera.Projection(0.0f, 1280.0f, 0.0f, 720.0f);
SetupShaders();
renderer2D.Create();
glClearColor(0.5, 0.5, 0.5, 1.0);
/*short textureIDS[] = {
texture.LoadFromFile("Media/MenuImage.png"),
texture.LoadFromFile("Media/image1.png"),
texture.LoadFromFile("Media/image2.png"),
};
for (int y = 0; y < 720; y += 4.) {
for (int x = 0; x < 1280; x += 4.) {
sprite.push_back(new Sprite2D(Rect(x, y, 4.0f), Rect(0.0f, 0.0f, 1.0f, 1.0f), rand() % 3 + 1));
}
}
std::cout << sprite.size() << std::endl;*/
}
void App2D::MainLoop()
{
int frames = 0;
float time = 0.0f;
while (!quit) {
timer.Reset();
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
ExitGame();
}
UpdateInput();
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
SetupCameraShader();
camera.Update();
Update(1.0f);
renderer2D.RenderClear();
renderer2D.DrawString("STRING", Vector2(0.0f, 0.0f), Color(255, 0, 0, 255));
renderer2D.Render();
window.SwapBuffers();
frames++;
if (timer.Elapsed() - time > 1.0f) {
time++;
Log("FPS: " + std::to_string(frames) + "\n");
frames = 0;
}
}
}
void App2D::SetupShaders()
{
shader.Free();
GLuint shaders[2] = {
shader.CreateShader("vertex.shader", GL_VERTEX_SHADER),
shader.CreateShader("fragment.shader", GL_FRAGMENT_SHADER)
}; shader.CreateAndUseProgram(shaders, 2);
GLint textures[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
};
glUniform1iv(0, 10, textures);
}
void App2D::SetupCameraShader()
{
GLint u_camera = shader.GetUniformLocation("u_camera");
Mat4 cameraMatrix = Mat4(1.0f);
cameraMatrix = camera.getCameraMatrix();
//cameraMatrix = textCamera.getCameraMatrix();
glUniformMatrix4fv(u_camera, 1, GL_FALSE, &(cameraMatrix[0][0]));
}