I'm trying to create AABB on 3D models in OpenGL. When I use a 3D box.obj where the vertices are all 1.0 I have no issues detecting collision between models. The trouble arises, however, when I use more complex 3D models with different values for the vertices. The complex models are smaller than the box.obj and have vertex values like .18 and .06 so the bounding boxes on them are so large, my engine detects a collisions between the objects when they aren't touching. A solution to this problem is to keep track of the max/min for each x, y, and z axes when reading the model into my game, but I'm having trouble doing so.
Below is my code for reading in models:
bool Model::buffer(string objFile)
{
vector<vec3> locs;
vector<vec2> uvs;
vector<vec3> norms;
vector<VertInd> vertInds;
// Open file for reading
ifstream inFile;
inFile.open(objFile);
string line;
if (inFile.is_open())
{
// Enter a loop that reads every line iteration from file until file is empty
while (getline(inFile, line))
{
istringstream ss(line);
string lineLabel;
// Read a string (the line label) from the line
ss >> lineLabel;
if (lineLabel == "v") // Vertices
{
float a, b, c;
ss >> a >> b >> c;
locs.push_back(vec3(a, b, c));
}
else if (lineLabel == "vt") // Texture Coordinates
{
float a, b;
ss >> a >> b;
uvs.push_back(vec2(a, b));
}
else if (lineLabel == "vn") // Vertex Normals
{
float a, b, c;
ss >> a >> b >> c;
norms.push_back(vec3(a, b, c));
}
// Get indices
else if (lineLabel == "f")
{
// do three times
for (int i = 0; i < 3; i++)
{
unsigned int a, b, c;
char s1, s2;
// Read int, then char slash
ss >> a >> s1 >> b >> s2 >> c;
// Decrement each of the ints by 1
vertInds.push_back(VertInd{ a - 1, b - 1, c - 1 });
}
}
/* GLfloat min_x, max_x, min_y, max_y, min_z, max_z;
min_x = max_x = locs[0].x;
min_y = max_y = locs[0].y;
min_z = max_z = locs[0].z;
for (int i = 0; i < locs.size(); i++)
{
if (locs[i].x < min_x) min_x = locs[i].x;
if (locs[i].x > max_x) max_x = locs[i].x;
if (locs[i].y < min_y) min_y = locs[i].y;
if (locs[i].y > max_y) max_y = locs[i].y;
if (locs[i].z < min_z) min_z = locs[i].z;
if (locs[i].z > max_z) max_z = locs[i].z;
}
vec3 size = vec3(max_x - min_x, max_y - min_y, max_z - min_z);
vec3 center = vec3((min_x + max_x) / 2, (min_y + max_y) / 2, (min_z + max_z) / 2);
mat4 transform = translate(mat4(1), center) * scale(mat4(1), size);
mat4 m = camera.camMat * transform;
glUniformMatrix4fv(2, 1, GL_FALSE, &m[0][0]); */
}
// Close the file
inFile.close();
}
vertCount = vertInds.size();
GLuint vertBuf;
vector<Vertex> vertBufData(vertCount);
for (unsigned int i = 0; i < vertCount; i++)
vertBufData[i] = { locs[vertInds[i].locInd], uvs[vertInds[i].uvInd], norms[vertInds[i].normInd] };
// Vertex array
glGenVertexArrays(1, &vertArr);
glGenBuffers(1, &vertBuf);
// Buffer data
glBindVertexArray(vertArr);
glBindBuffer(GL_ARRAY_BUFFER, vertBuf);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) *vertCount, &vertBufData[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)sizeof(vec3)); // (void*)sizeof(VertInd));
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(sizeof(vec3) + sizeof(vec2)));
glBindVertexArray(0);
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
return true;
}
The section commented out /**/ is the part I've recently added in an attempt to get the min/max vert values and center my model, but now the scene won't even load.
See usage example here:
// return the min/max points of pts
template< typename Vec >
pair< Vec, Vec > GetExtents( const Vec* pts, size_t stride, size_t count )
{
unsigned char* base = (unsigned char*)pts;
Vec pmin( *(Vec*)base );
Vec pmax( *(Vec*)base );
for( size_t i = 0; i < count; ++i, base += stride )
{
const Vec& pt = *(Vec*)base;
pmin = glm::min( pmin, pt );
pmax = glm::max( pmax, pt );
}
return make_pair( pmin, pmax );
}
// centers geometry around the origin
// and scales it to fit in a size^3 box
template< typename Vec >
void CenterAndScale( Vec* pts, size_t stride, size_t count, const typename Vec::value_type& size )
{
typedef typename Vec::value_type Scalar;
// get min/max extents
pair< Vec, Vec > exts = GetExtents( pts, stride, count );
// center and scale
const Vec center = ( exts.first * Scalar( 0.5 ) ) + ( exts.second * Scalar( 0.5f ) );
const Scalar factor = size / glm::compMax( exts.second - exts.first );
unsigned char* base = (unsigned char*)pts;
for( size_t i = 0; i < count; ++i, base += stride )
{
Vec& pt = *(Vec*)base;
pt = ( ( pt - center ) * factor );
}
}
Related
I've been trying to achieve smooth normals on a generated terrain mesh (tile grid style) to give off smooth lighting, however I have only be able to achieve flat shaded lighting and I believe this might be to do with the fact all the faces are separated (though some vertex's may have the same position).
I've looked around for a similar question, the closest would be: this question however I'm not sure about how to go about implementing his solution of taking a percentage.
Currently I have this code which is based on the typical way to calculate smooth normals - but only achieves flat shading in my case.
// Reset Normals
for (int i = 0; i < this->vertexCount; i++) {
this->vertices[i].normal = glm::vec3(0.0f);
}
// For each face
for (int i = 0; i < totalVerts; i += 3) {
auto index0 = indices ? this->indices[i] : i;
auto index1 = indices ? this->indices[i + 1] : i + 1;
auto index2 = indices ? this->indices[i + 2] : i + 2;
auto vertex0 = this->vertices[index0].position;
auto vertex1 = this->vertices[index1].position;
auto vertex2 = this->vertices[index2].position;
auto normal = glm::cross(vertex1 - vertex0, vertex2 - vertex0);
this->vertices[index0].normal += normal;
this->vertices[index1].normal += normal;
this->vertices[index2].normal += normal;
}
// Normalize
for (int i = 0; i < this->vertexCount; i++) {
this->vertices[i].normal = glm::normalize(this->vertices[i].normal);
}
How would I go about changing this to smooth between surrounding faces? (As for why each face is separate, it's because some may have verts with specific properties which cannot be shared with other faces)
For your reference, a working code to get the face normals, vertices, and vertex normals is:
void get_vertices_and_normals_from_triangles(vector<triangle> &t, vector<vec3> &fn, vector<vec3> &v, vector<vec3> &vn)
{
// Face normals
fn.clear();
// Vertices
v.clear();
// Vertex normals
vn.clear();
if(0 == t.size())
return;
cout << "Triangles: " << t.size() << endl;
cout << "Welding vertices" << endl;
// Insert unique vertices into set.
set<indexed_vertex_3> vertex_set;
for(vector<triangle>::const_iterator i = t.begin(); i != t.end(); i++)
{
vertex_set.insert(i->vertex[0]);
vertex_set.insert(i->vertex[1]);
vertex_set.insert(i->vertex[2]);
}
cout << "Vertices: " << vertex_set.size() << endl;
cout << "Generating vertex indices" << endl;
vector<indexed_vertex_3> vv;
// Add indices to the vertices.
for(set<indexed_vertex_3>::const_iterator i = vertex_set.begin(); i != vertex_set.end(); i++)
{
size_t index = vv.size();
vv.push_back(*i);
vv[index].index = index;
}
for (size_t i = 0; i < vv.size(); i++)
{
vec3 vv_element(vv[i].x, vv[i].y, vv[i].z);
v.push_back(vv_element);
}
vertex_set.clear();
// Re-insert modifies vertices into set.
for(vector<indexed_vertex_3>::const_iterator i = vv.begin(); i != vv.end(); i++)
vertex_set.insert(*i);
cout << "Assigning vertex indices to triangles" << endl;
// Find the three vertices for each triangle, by index.
set<indexed_vertex_3>::iterator find_iter;
for(vector<triangle>::iterator i = t.begin(); i != t.end(); i++)
{
find_iter = vertex_set.find(i->vertex[0]);
i->vertex[0].index = find_iter->index;
find_iter = vertex_set.find(i->vertex[1]);
i->vertex[1].index = find_iter->index;
find_iter = vertex_set.find(i->vertex[2]);
i->vertex[2].index = find_iter->index;
}
vertex_set.clear();
cout << "Calculating normals" << endl;
fn.resize(t.size());
vn.resize(v.size());
for(size_t i = 0; i < t.size(); i++)
{
vec3 v0;// = t[i].vertex[1] - t[i].vertex[0];
v0.x = t[i].vertex[1].x - t[i].vertex[0].x;
v0.y = t[i].vertex[1].y - t[i].vertex[0].y;
v0.z = t[i].vertex[1].z - t[i].vertex[0].z;
vec3 v1;// = t[i].vertex[2] - t[i].vertex[0];
v1.x = t[i].vertex[2].x - t[i].vertex[0].x;
v1.y = t[i].vertex[2].y - t[i].vertex[0].y;
v1.z = t[i].vertex[2].z - t[i].vertex[0].z;
fn[i] = cross(v0, v1);
fn[i] = normalize(fn[i]);
vn[t[i].vertex[0].index] = vn[t[i].vertex[0].index] + fn[i];
vn[t[i].vertex[1].index] = vn[t[i].vertex[1].index] + fn[i];
vn[t[i].vertex[2].index] = vn[t[i].vertex[2].index] + fn[i];
}
for (size_t i = 0; i < vn.size(); i++)
vn[i] = normalize(vn[i]);
}
The code to stuff the vertex data into a vector is as follows. Note that the unwelded vertices of the triangles are reconstructed in the following calls to vertex_data.push_back(v0.x);, etc.
void draw_mesh(void)
{
glUseProgram(render.get_program());
glUniformMatrix4fv(uniforms.render.proj_matrix, 1, GL_FALSE, &main_camera.projection_mat[0][0]);
glUniformMatrix4fv(uniforms.render.mv_matrix, 1, GL_FALSE, &main_camera.view_mat[0][0]);
glUniform1f(uniforms.render.shading_level, 1.0f);
vector<float> vertex_data;
for (size_t i = 0; i < triangles.size(); i++)
{
vec3 colour(0.0f, 0.8f, 1.0f);
size_t v0_index = triangles[i].vertex[0].index;
size_t v1_index = triangles[i].vertex[1].index;
size_t v2_index = triangles[i].vertex[2].index;
vec3 v0_fn(vertex_normals[v0_index].x, vertex_normals[v0_index].y, vertex_normals[v0_index].z);
vec3 v1_fn(vertex_normals[v1_index].x, vertex_normals[v1_index].y, vertex_normals[v1_index].z);
vec3 v2_fn(vertex_normals[v2_index].x, vertex_normals[v2_index].y, vertex_normals[v2_index].z);
vec3 v0(triangles[i].vertex[0].x, triangles[i].vertex[0].y, triangles[i].vertex[0].z);
vec3 v1(triangles[i].vertex[1].x, triangles[i].vertex[1].y, triangles[i].vertex[1].z);
vec3 v2(triangles[i].vertex[2].x, triangles[i].vertex[2].y, triangles[i].vertex[2].z);
vertex_data.push_back(v0.x);
vertex_data.push_back(v0.y);
vertex_data.push_back(v0.z);
vertex_data.push_back(v0_fn.x);
vertex_data.push_back(v0_fn.y);
vertex_data.push_back(v0_fn.z);
vertex_data.push_back(colour.x);
vertex_data.push_back(colour.y);
vertex_data.push_back(colour.z);
vertex_data.push_back(v1.x);
vertex_data.push_back(v1.y);
vertex_data.push_back(v1.z);
vertex_data.push_back(v1_fn.x);
vertex_data.push_back(v1_fn.y);
vertex_data.push_back(v1_fn.z);
vertex_data.push_back(colour.x);
vertex_data.push_back(colour.y);
vertex_data.push_back(colour.z);
vertex_data.push_back(v2.x);
vertex_data.push_back(v2.y);
vertex_data.push_back(v2.z);
vertex_data.push_back(v2_fn.x);
vertex_data.push_back(v2_fn.y);
vertex_data.push_back(v2_fn.z);
vertex_data.push_back(colour.x);
vertex_data.push_back(colour.y);
vertex_data.push_back(colour.z);
}
GLuint components_per_vertex = 9;
const GLuint components_per_normal = 3;
GLuint components_per_position = 3;
const GLuint components_per_colour = 3;
GLuint triangle_buffer;
glGenBuffers(1, &triangle_buffer);
GLuint num_vertices = static_cast<GLuint>(vertex_data.size()) / components_per_vertex;
glBindBuffer(GL_ARRAY_BUFFER, triangle_buffer);
glBufferData(GL_ARRAY_BUFFER, vertex_data.size() * sizeof(GLfloat), &vertex_data[0], GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(glGetAttribLocation(render.get_program(), "position"));
glVertexAttribPointer(glGetAttribLocation(render.get_program(), "position"),
components_per_position,
GL_FLOAT,
GL_FALSE,
components_per_vertex * sizeof(GLfloat),
NULL);
glEnableVertexAttribArray(glGetAttribLocation(render.get_program(), "normal"));
glVertexAttribPointer(glGetAttribLocation(render.get_program(), "normal"),
components_per_normal,
GL_FLOAT,
GL_TRUE,
components_per_vertex * sizeof(GLfloat),
(const GLvoid*)(components_per_position * sizeof(GLfloat)));
glEnableVertexAttribArray(glGetAttribLocation(render.get_program(), "colour"));
glVertexAttribPointer(glGetAttribLocation(render.get_program(), "colour"),
components_per_colour,
GL_FLOAT,
GL_TRUE,
components_per_vertex * sizeof(GLfloat),
(const GLvoid*)(components_per_normal * sizeof(GLfloat) + components_per_position * sizeof(GLfloat)));
glDrawArrays(GL_TRIANGLES, 0, num_vertices);
glDeleteBuffers(1, &triangle_buffer);
}
I'm not sure if you're getting notifications of my edits.
i keep getting this error when trying to load an OBJ file into my project
atioglxx.pdb not loaded
with the following exception
Exception thrown at 0x53A083FF (atioglxx.dll) in Reality.exe: 0xC0000005: Access violation reading location 0x0894F000.
sometimes i get this error and sometimes i don't and have my model on the screen. So, i tried to debug the code and found that glBufferData function is what causes this error but couldn't figure out what is the problem with it.
Here the OBJ Loaded function
bool Mesh::LoadOBJ(std::string objFile)
{
std::vector<glm::vec3> position;
std::vector<glm::vec2> UVs;
std::vector<glm::vec3> normals;
std::vector< float > vertices;
std::vector<unsigned int> indices;
std::unordered_map< std::string, unsigned int> isProcessed;
std::ifstream myFile;
myFile.open(objFile);
if (!myFile.is_open())
{
std::cout << "Error Openening OBJ file : " << objFile;
return false;
}
unsigned int cnt = 1;
while (!myFile.eof())
{
std::string type;
myFile >> type;
float x, y, z;
if (type == "v") {
myFile >> x >> y >> z;
glm::vec3 v(x, y, z);
position.push_back(v);
}
else if (type == "vt") {
myFile >> x >> y;
glm::vec2 v(x, y);
UVs.push_back(v);
}
else if (type == "vn") {
myFile >> x >> y >> z;
glm::vec3 v(x, y, z);
normals.push_back(v);
}
else if (type == "f") {
std::string p1, p2, p3;
std::vector<std::string> vertex(3);
myFile >> p1;
if (!isProcessed[p1]) {
isProcessed[p1] = cnt;
indices.push_back(cnt - 1);
vertex[0] = "";
vertex[1] = "";
vertex[2] = "";
int c = 0;
for (int i = 0; i < p1.size(); ++i) {
if (p1[i] == '/') {
++c;
continue;
}
vertex[c] += p1[i];
}
if (vertex[0].size() > 0) {
int vertexIndex = std::stoi(vertex[0]);
--vertexIndex;
vertices.push_back(position[vertexIndex].x);
vertices.push_back(position[vertexIndex].y);
vertices.push_back(position[vertexIndex].z);
}
if (vertex[1].size() > 0) {
int UVsIndex = std::stoi(vertex[1]);
--UVsIndex;
vertices.push_back(UVs[UVsIndex].x);
vertices.push_back(UVs[UVsIndex].y);
}
if (vertex[2].size() > 0) {
int normalIndex = std::stoi(vertex[2]);
--normalIndex;
vertices.push_back(normals[normalIndex].x);
vertices.push_back(normals[normalIndex].y);
vertices.push_back(normals[normalIndex].z);
}
++cnt;
}
else {
indices.push_back(isProcessed[p1] - 1);
}
myFile >> p2;
if (!isProcessed[p2]) {
isProcessed[p2] = cnt;
indices.push_back(cnt - 1);
vertex[0] = "";
vertex[1] = "";
vertex[2] = "";
int c = 0;
for (int i = 0; i < p2.size(); ++i) {
if (p2[i] == '/') {
++c;
continue;
}
vertex[c] += p2[i];
}
if (vertex[0].size() > 0) {
int vertexIndex = std::stoi(vertex[0]);
--vertexIndex;
vertices.push_back(position[vertexIndex].x);
vertices.push_back(position[vertexIndex].y);
vertices.push_back(position[vertexIndex].z);
}
if (vertex[1].size() > 0) {
int UVsIndex = std::stoi(vertex[1]);
--UVsIndex;
vertices.push_back(UVs[UVsIndex].x);
vertices.push_back(UVs[UVsIndex].y);
}
if (vertex[2].size() > 0) {
int normalIndex = std::stoi(vertex[2]);
--normalIndex;
vertices.push_back(normals[normalIndex].x);
vertices.push_back(normals[normalIndex].y);
vertices.push_back(normals[normalIndex].z);
}
++cnt;
}
else {
indices.push_back(isProcessed[p2] - 1);
}
myFile >> p3;
if (!isProcessed[p3]) {
isProcessed[p3] = cnt;
indices.push_back(cnt - 1);
vertex[0] = "";
vertex[1] = "";
vertex[2] = "";
int c = 0;
for (int i = 0; i < p3.size(); ++i) {
if (p3[i] == '/') {
++c;
continue;
}
vertex[c] += p3[i];
}
if (vertex[0].size() > 0) {
int vertexIndex = std::stoi(vertex[0]);
--vertexIndex;
vertices.push_back(position[vertexIndex].x);
vertices.push_back(position[vertexIndex].y);
vertices.push_back(position[vertexIndex].z);
}
if (vertex[1].size() > 0) {
int UVsIndex = std::stoi(vertex[1]);
--UVsIndex;
vertices.push_back(UVs[UVsIndex].x);
vertices.push_back(UVs[UVsIndex].y);
}
if (vertex[2].size() > 0) {
int normalIndex = std::stoi(vertex[2]);
--normalIndex;
vertices.push_back(normals[normalIndex].x);
vertices.push_back(normals[normalIndex].y);
vertices.push_back(normals[normalIndex].z);
}
++cnt;
}
else {
indices.push_back(isProcessed[p3] - 1);
}
}
mVAO = new VertexArrayObject(vertices , vertices.size() , indices , static_cast<unsigned int>(indices.size()));
myFile.close();
return true ;
and here is the constructor of my VertexArray class
VertexArrayObject::VertexArrayObject(std::vector<float>& vertices, int VBOsize, std::vector<unsigned int>& indecies, unsigned int EBOsize):
EBOsize(EBOsize)
{
glGenVertexArrays(1, &mVAOiD);
glBindVertexArray(mVAOiD);
glGenBuffers(1, &mVBOiD);
glBindBuffer(GL_ARRAY_BUFFER, mVBOiD);
glBufferData(GL_ARRAY_BUFFER, 8 * VBOsize * sizeof(float) , &vertices[0], GL_STATIC_DRAW);
glGenBuffers(1, &mEBOiD);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEBOiD);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, EBOsize * sizeof(unsigned int), &indecies[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void*>(sizeof(float) * 3));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void*>(sizeof(float) * 5));
}
and this is the OBJ file for the model i'm trying to render
Rock.obj
Note
this is my first question on stackoverflow so please take it easy on me.
The computation of the buffer size in bytes is wrong. verizes.size() is not the number of vertex attribute, it is the number of float elements in the std::vector.
You pass vertices.size() to the argument VBOsize of VertexArrayObjects constructor:
mVAO = new VertexArrayObject(vertices , vertices.size(), indices ,static_cast<unsigned int>(indices.size()));
In the constructor VBOsize is multiplied by 8:
VertexArrayObject::VertexArrayObject(std::vector<float>& vertices, int VBOsize, std::vector<unsigned int>& indecies, unsigned int EBOsize)
:EBOsize(EBOsize)
{
// [...]
glBufferData(GL_ARRAY_BUFFER, 8 * VBOsize * sizeof(float) , &vertices[0], GL_STATIC_DRAW);
// [...]
If VBOsize is the number of vertices, then you have to divide vertices.size() by 8:
mVAO = new VertexArrayObject(vertices, vertices.size() , indices , static_cast<unsigned int>(indices.size()));
mVAO = new VertexArrayObject(vertices, vertices.size() / 8, indices, static_cast<unsigned int>(indices.size()));
Anyway, I recommend to change the computation of the buffer size:
glBufferData(GL_ARRAY_BUFFER, 8 * VBOsize * sizeof(float) , &vertices[0], GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);
I am asking for some help with drawing a flat terrain using triangle strips. If this is the correct way to do it?
So far i managed to create vertex and index arrays, debugged it couple of times and it seems to generate correct values.
At first I tried drawing with degenerate triangles, but I read somewhere that it's an outdated way and I should rather switch to a primitive restart option.
NOTE THAT THIS NOT THE EXACT WAY MY PROJECT IS SET UP... I just copied the necessery code
Here is the code for terrain generation:
this->terrainSize = width * height;
unsigned int numRows = height - 1; //!NUMBER OF ROWS OF TRIANGLE STRIPS
unsigned int numColumns = width - 1; //!Number of columns of strips
unsigned int numVerticiesPerRow = 2 * width; //!NUMBER OF VERTICIES PER EVERY ROW
unsigned int numIndPerRow = numColumns * 2 + 2; //!Number of indices per row
unsigned int numOfStrips = numRows - 1; //!Number of splits, after every row
this->numVertices = terrainSize * 8;
this->numIndices = numRows * numIndPerRow + numOfStrips; //!total number of indices
std::vector<float> vertexArray (numVertices);
float* vertexPointer = vertexArray.data();
float* vertexPointer2 = vertexArray.data();
for( int row = 0; row < height; row++ ) //!Z AXIS
{
for( int col = 0; col < width; col++ ) //!X AXIS
{
float x = (float) ( col - width / 2 );
float z = (float) ( row - height / 2 );
//?Positions
*vertexPointer = x; ++vertexPointer; //?1
*vertexPointer = 0.0f; ++vertexPointer; //?2
*vertexPointer = z; ++vertexPointer; //?3
//?Normals (lightning)
*vertexPointer = 0.0f; ++vertexPointer; //?4
*vertexPointer = 1.0f; ++vertexPointer; //?5
*vertexPointer = 0.0f; ++vertexPointer; //?6
//?Texture Coordinates
*vertexPointer = x; ++vertexPointer; //?7
*vertexPointer = z; ++vertexPointer; //?8
}
}
//?GENERATE INDICIES
std::vector<unsigned int> indexArray (numIndices);
unsigned int* indexPointer = indexArray.data();
unsigned int* indexPointer2 = indexArray.data();
for( int row = 0; row < height - 1; row++ ) //!Z AXIS
{
for( int col = 0; col < width; col++ ) //!X AXIS
{
*indexPointer = ( row * height ) + col; ++indexPointer;
*indexPointer = (( row + 1 )* height ) + col; ++indexPointer;
}
if( row < height - 2 ) **//!Add 0xFFFF on the end of each row**
{
*indexPointer = 0xFFFF; ++indexPointer;
}
}
//Pack the data into vertex struct
unsigned int i;
for( i = 0; i < this->numVertices; i=i+0 )
{
Vertex vertex;
glm::vec3 vector;
vector.x = vertexArray[i++];
vector.y = vertexArray[i++];
vector.z = vertexArray[i++];
vertex.Position = vector;
vector.x = vertexArray[i++];
vector.y = vertexArray[i++];
vector.z = vertexArray[i++];
vertex.Normal = vector;
glm::vec2 vec;
vec.x = vertexArray[i++];
vec.y = vertexArray[i++];
vertex.TexCords = vec;
this->vertexStruct.push_back( vertex );
}
for( i = 0; i < this->numIndices; i++ )
{
this->indexVector.push_back( indexArray[i] );
}
//Bind VertexArray
VAO.Create();
//Bind VertexBuffer
VBO.Create( vertexArray, numVertices);
//Bind IndexBuffer
IBO.Create( indexArray, numIndices );
//? DEFINE HOW SHOULD THE GPU SHOULD READ THE DATA FROM VERTEX BUFFER
//Position
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, sizeof( Vertex ), (GLvoid*) offsetof( Vertex, Position ) );
glEnableVertexAttribArray( 0 );
//Normal
glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, sizeof( Vertex ), (GLvoid*) offsetof( Vertex, Normal ) );
glEnableVertexAttribArray( 1 );
//Texcoord
glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE, sizeof( Vertex ), (GLvoid*) offsetof( Vertex, TexCords ) );
glEnableVertexAttribArray( 2 );
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); //?ENABLE WIREFRAME
glEnable( GL_PRIMITIVE_RESTART );
glPrimitiveRestartIndex( 0xFFFF );
glDrawElements( GL_TRIANGLE_STRIP, this->numVertices, GL_UNSIGNED_INT, this->indexArray );
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); //?DISABLE WIREFRAME
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);
Is there an algorithm that could be used to generate a plane using the GL_TRIANGLES primitive type?
Here's my current function:
Mesh* Mesh::CreateMeshPlane(vec2 bottomleft, ivec2 numvertices, vec2 worldsize){
int numVerts = numvertices.x * numvertices.y;
float xStep = worldsize.x / (numvertices.x - 1);
float yStep = worldsize.y / (numvertices.y - 1);
VertexFormat* verts = new VertexFormat[numVerts];
for (int y = 0; y < numvertices.y; y++)
{
for (int x = 0; x < numvertices.x; x++)
{
verts[x + (y * numvertices.x)].pos.x = bottomleft.x + (xStep * x);
verts[x + (y * numvertices.x)].pos.y = bottomleft.y + (yStep * y);
verts[x + (y * numvertices.x)].pos.z = 0;
}
}
Mesh* pMesh = new Mesh();
pMesh->Init(verts, numVerts, indices, 6, GL_STATIC_DRAW);
glPointSize(10.0f);
pMesh->m_PrimitiveType = GL_POINTS;
delete[] verts;
return pMesh;}
I'm just unsure how to implement indices into the for loop to be able to know which points to draw.
What I think I need to know:
Each square will be made up of 2 triangles, each square requiring 6 indices
Currently I'm drawing from the bottom left
I need to know how many squares I'll have from the numbers passed in
Maybe something like this:
int width = 4;
int length = 6;
int height = 1;
std::vector<float> planeVertices;
for (int x = 0; x < width - 1; x++) {
for (int z = 0; z < length - 1; z++) {
planeVertices.push_back(x);
planeVertices.push_back(height);
planeVertices.push_back(z);
planeVertices.push_back(x);
planeVertices.push_back(height);
planeVertices.push_back(z + 1);
planeVertices.push_back(x + 1);
planeVertices.push_back(height);
planeVertices.push_back(z + 1);
planeVertices.push_back(x);
planeVertices.push_back(height);
planeVertices.push_back(z);
planeVertices.push_back(x + 1);
planeVertices.push_back(height);
planeVertices.push_back(z);
planeVertices.push_back(x + 1);
planeVertices.push_back(height);
planeVertices.push_back(z + 1);
}
}
...
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, planeVertices.size() * sizeof(float), planeVertices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
glEnableVertexAttribArray(0);
...
glDrawArrays(GL_TRIANGLES, 0, (width - 1) * (length - 1) * 6);
This code creates an std::vector<float> and adds the plane vertices to it. The nested for loops add two triangles for every unit of the plane (so with width as 4 and length as 6 the plane will be 4 units by 6 units, and will be made of 6 * 4 * 2 = 48 triangles). The height of the plane is set by the height variable. This only generates flat planes, but a simple transformation lets you rotate and scale this as you need.
WARNING: this code is untested.
Just to close this question here's how I did it:
Mesh* Mesh::CreateMeshPlane(vec3 bottomleft, ivec2 numvertices, vec2
worldsize, vec2 texturerepetition)
{
int numVerts = numvertices.x * numvertices.y;
int numFaces = (numvertices.x - 1) * (numvertices.y - 1);
int numIndices = numFaces * 6;
float xStep = worldsize.x / (numvertices.x - 1);
float yStep = worldsize.y / (numvertices.y - 1);
float zStep = worldsize.y / (numvertices.y - 1);
float uStep = texturerepetition.x / (numvertices.x - 1);
float vStep = texturerepetition.y / (numvertices.y - 1);
VertexFormat* verts = new VertexFormat[numVerts];
unsigned int* indices = new unsigned int[numIndices];
for (int y = 0; y < numvertices.y; y++)
{
for (int x = 0; x < numvertices.x; x++)
{
verts[x + (y * numvertices.x)].pos.x = bottomleft.x + (xStep * x);
verts[x + (y * numvertices.x)].pos.y = bottomleft.y;
verts[x + (y * numvertices.x)].pos.z = bottomleft.z + (zStep * y);
verts[y * numvertices.x + x].uv.x = uStep * x;
verts[y * numvertices.x + x].uv.y = vStep * y;
}
}
int offset = 0;
for (int i = 0; i < numIndices; i++)
{
// The bottom left index of the current face
// + the offset to snap back when we hit the edge
unsigned int cornerIndex = i/6 + offset;
// If we reach the edge we increase the offset so that it goes to the next bottom left
if ((cornerIndex + 1)%numvertices.x == 0)
{
offset++;
cornerIndex++; // Adding new offset to the bottom left
}
// First triangle
indices[i] = (unsigned int)cornerIndex;
i++;
indices[i] = (unsigned int)cornerIndex + numvertices.x;
i++;
indices[i] = (unsigned int)cornerIndex + numvertices.x + 1;
i++;
// Second triangle
indices[i] = (unsigned int)cornerIndex;
i++;
indices[i] = (unsigned int)cornerIndex + numvertices.x + 1;
i++;
indices[i] = (unsigned int)cornerIndex + 1;
}
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
Mesh* pMesh = new Mesh();
pMesh->Init(verts, numVerts, indices, numIndices, GL_STATIC_DRAW);
delete[] verts;
return pMesh;
}
Workflow:
1. Calculating number of faces I need, then the number of indices
2. Creating an offset that is added to the cornerIndex when we realize we hit the edge of the vertex array (by using modulus numvertices.y)
3. Doing simple math to draw corners in correct order based on the cornerIndex
Notes:
1. Im drawing using GL_TRIANGLES as the primitive type
2. Drawing from bottom left to top right
3. cornerIndex therefore is the bottom left of the current square we're drawing on
Hope someone can find this helpful!