Creating flat terrain with triangle strips - c++

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

Related

How to generate procedural terrain with Perlin Noise in OpenGL?

I need to generate procedural terrain using Noise (using Perlin noise) in OpenGL. Each time the application runs a new terrain, it needs to be generated using a new seed. (Do not use external library.) Is there a method/requirement needed when making a class for noise terrains. What functions/calculation i need to call and in which order ?
PS: I use Visual Studio 2019.
// Copy the array data into a float array, and scale and offset the heights.
mHeightmap.resize(NumRows * NumCols, 0);
for( int i = 0; i < NumRows * NumCols; ++i)
{
mHeightmap[i] = (float)in[i] * HeightScale;
}
// A height for each vertex
{
std::vector<unsigned char> in(NumRows * NumCols);
// Open the file.
std::ifstream inFile;
inFile.open(heightmapName.c_str(), std::ios_base::binary);
if (inFile)
{
// Read the RAW bytes.
inFile.read((char*)&in[0], (std::streamsize)in.size());
// Done with file.
inFile.close();
}
// Copy the array data into a float array, and scale and offset the heights.
mHeightmap.resize(NumRows * NumCols, 0);
for( int i = 0; i < NumRows * NumCols; ++i)
{
mHeightmap[i] = (float)in[i] * HeightScale;
}
void Terrain::CreateVAO()
{
std::vector<GLfloat> vertices;
vertices.reserve(NumCols * NumRows * 8);
float invTwoDX = 1.0f / (2.0f * CellSpacing);
float invTwoDZ = 1.0f / (2.0f * CellSpacing);
//vertices
for ( int z = 0; z < NumRows; z++)
{
for ( int x = 0; x < NumCols; x++)
{
//vertex data
int i = z * NumCols + x;
vertices.push_back((float)x*CellSpacing);
vertices.push_back(mHeightmap[i]);
vertices.push_back((float)z * CellSpacing);
//normal data
glm::vec3 _N = { 0.0f,1.0f, 0.0f };
if(z >= 1 && z < NumRows -1 && x >= 1 && z < NumCols - 1)
{
float t = mHeightmap[(z - 1) * NumCols + x];
float b = mHeightmap[(z + 1) * NumCols + x];
float l = mHeightmap[z * NumCols + x - 1];
float r = mHeightmap[z * NumCols + x + 1];
glm::vec3 tanZ(0.0f, (b - t) * invTwoDZ, 1.0f);
glm::vec3 tanX(1.0f, (r - l) * invTwoDX, 0.0f);
glm::vec3 _C, _N;
_C = glm::cross(tanZ, tanX);
_N = glm::normalize(_C);
}
vertices.push_back(_N.x);
vertices.push_back(_N.y);
vertices.push_back(_N.z);
vertices.push_back((float)x);
vertices.push_back((float)z);
}
}
std::vector<GLuint> indices;
vertices.reserve((NumCols-1)*(NumRows -1)*6);
//indices
for ( int z = 0; z < NumRows-1; z++)
{
for ( int x = 0; x < NumCols-1; x++)
{
GLint a = z * NumCols + x;
GLint b = (z +1) * NumCols + x;
GLint c = z * NumCols + (x+1);
GLint d = (z+1) * NumCols + (x+1);
indices.push_back(c);
indices.push_back(a);
indices.push_back(b);
indices.push_back(c);
indices.push_back(b);
indices.push_back(d);
}
}
indexcount = indices.size();
GLuint VBO, EBO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(GLfloat), vertices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(
0,
3,
GL_FLOAT,
GL_FALSE,
8 * sizeof(GLfloat), //Strude of the single vertex(pos)
(GLvoid*)0); //Offset from beginning of Vertex
glEnableVertexAttribArray(0);
glVertexAttribPointer(
1,
3,
GL_FLOAT,
GL_FALSE,
8 * sizeof(GLfloat), //Strude of the single vertex(pos+color)
(GLvoid*)(3 * sizeof(GLfloat))); //Offset from beginning of Vertex
glEnableVertexAttribArray(1);
glVertexAttribPointer(
2,
2, //2 float component for coordinates
GL_FLOAT,
GL_FALSE,
8 * sizeof(GLfloat), //Strude of the single vertex(pos+color+texture)
(GLvoid*)(6 * sizeof(GLfloat)));//Offset from beginning of Vertex
glEnableVertexAttribArray(2);
I'm not sure if I see usage of Perlin noise in your code. Try this lightweight, easy to integrate library:
https://github.com/Auburn/FastNoise which has Perlin and tons of other useful stuff like a visualizer.
Usage is as simple as
noise.GetNoise((float)x, (float)y); which you can plug into your height function

Issue in drawing ellipsoid with OpenGL

this is the code I use to create and draw an ellipsoid with OpenGL with shader
const float _2pi = 2.0f * M_PI;
std::vector<glm::vec3> positions;
std::vector<glm::vec3> normals;
std::vector<glm::vec2> textureCoords;
for(int i = 0; i <= stacks; ++i) {
// V texture coordinate
float V = i / (float)stacks;
float phi = V * M_PI;
for( int j = 0; j <= slices; ++j) {
// U texture coordinate
float U = j / (float)slices;
float theta = U * _2pi;
float X = a * cos(theta) * cos(phi);
float Y = b * cos(theta) * sin(phi);
float Z = c * sin(theta);
positions.push_back( glm::vec3( X, Y, Z) );
normals.push_back( glm::vec3(X, Y, Z) );
textureCoords.push_back( glm::vec2(U, V) );
}
}
// Now generate the index buffer
std::vector<GLuint> indicies;
for(int i=0; i <slices*stacks+slices; ++i) {
indicies.push_back(i);
indicies.push_back(i + slices + 1);
indicies.push_back(i + slices);
indicies.push_back(i + slices + 1);
indicies.push_back(i);
indicies.push_back(i + 1);
}
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(4, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(glm::vec3), positions.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), normals.data(), GL_STATIC_DRAW);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_TRUE, 0, nullptr);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ARRAY_BUFFER, textureCoords.size() * sizeof(glm::vec2), textureCoords.data(), GL_STATIC_DRAW);
glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(8);
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glBufferData( GL_ELEMENT_ARRAY_BUFFER, indicies.size() * sizeof(GLuint), indicies.data(), GL_STATIC_DRAW);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
while this is the code that I use to render it:
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
if(style == glObject::STYLE::WIREFRAME) glDrawElements(GL_LINES, (slices * stacks + slices) * 6, GL_UNSIGNED_INT, nullptr);
if(style == glObject::STYLE::SOLID) glDrawElements(GL_TRIANGLES, (slices * stacks + slices) * 6, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
It seems to work but I have some issue.
Looking the image it is possible to see some vertex in the wrong position.
I think that is something related to the indicies but I'm not sure.
I have notice that depends by the number of stacks or slices that I use
UPDATE:
I take into account the suggestion of #Rabbid76 and this is the result.
No more degenerated vertex and triangles in the rendering.
However the rendering is not equal to the one of #Rabbid76 there is like a rotation of the vertex.
FINAL:
This is the creation vertex and indices code:
std::vector<glm::vec3> positions;
std::vector<glm::vec3> normals;
std::vector<glm::vec2> textureCoords;
for(int i = 0; i <= stacks; ++i) {
// V texture coordinate.
float V = i / (float)stacks;
float phi = V * M_PI;
for( int j = 0; j <= slices; ++j) {
// U texture coordinate.
float U = j / (float)slices;
float theta = U * 2.0f * M_PI;
float X = cos(theta) * sin(phi);
float Y = cos(phi);
float Z = sin(theta) * sin(phi);
positions.push_back( glm::vec3( X, Y, Z) * radius );
normals.push_back( glm::vec3(X, Y, Z) );
textureCoords.push_back( glm::vec2(U, V) );
}
}
// Now generate the index buffer
std::vector<GLuint> indicies;
int noPerSlice = slices + 1;
for(int i=0; i < stacks; ++i) {
for (int j=0; j < slices; ++j) {
int start_i = (i * noPerSlice) + j;
indicies.push_back( start_i );
indicies.push_back( start_i + noPerSlice + 1 );
indicies.push_back( start_i + noPerSlice );
indicies.push_back( start_i + noPerSlice + 1 );
indicies.push_back( start_i );
indicies.push_back( start_i + 1 );
}
}
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(4, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(glm::vec3), positions.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), normals.data(), GL_STATIC_DRAW);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_TRUE, 0, nullptr);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ARRAY_BUFFER, textureCoords.size() * sizeof(glm::vec2), textureCoords.data(), GL_STATIC_DRAW);
glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(8);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicies.size() * sizeof(GLuint), indicies.data(), GL_STATIC_DRAW);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
This is the rendering one:
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
if(style == glObject::STYLE::WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
if(style == glObject::STYLE::SOLID) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDrawElements(GL_TRIANGLES, (slices * stacks + slices) * 6, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
You have confused phi and theta. theta is the angle of the points around the circumference of a slice in range [0, 2*PI]. phi is the angle of the points form the south to the north in range [-PI, PI]:
for (int i = 0; i <= stacks; ++i) {
// V texture coordinate
float V = i / (float)stacks;
float phi = V * M_PI - M_PI/2.0;
for ( int j = 0; j <= slices; ++j) {
// U texture coordinate
float U = j / (float)slices;
float theta = U * _2pi;
float X = a * cos(phi) * cos(theta);
float Y = b * cos(phi) * sin(theta);
float Z = c * sin(phi);
positions.push_back( glm::vec3( X, Y, Z) );
normals.push_back( glm::vec3(X, Y, Z) );
textureCoords.push_back( glm::vec2(U, V) );
}
}
The number of points of a slice (around the circumference) is noPerSlice = slices + 1. The first index of a point of a quad is start_i = (i * noPerSlice) + j, where i is the index of the stack and j the index around the slice. Create slices quads around the circumference and stacks slices form the south to the north:
int noPerSlice = slices + 1;
for(int i=0; i < stacks; ++i) {
for (int j = 0; j < slices; ++j) {
int start_i = (i * noPerSlice) + j;
indicies.push_back( start_i );
indicies.push_back( start_i + noPerSlice + 1 );
indicies.push_back( start_i + noPerSlice );
indicies.push_back( start_i + noPerSlice + 1 );
indicies.push_back( start_i );
indicies.push_back( start_i + 1 );
}
}
i think
slices*stacks+slices
should be
slices*stacks+stacks
the +stacks is for the extra quads from the duplicate vertex in every stack
although this fixes the number of indices, you still have degenerate triangles along the duplicate vertices where theta equals zero

How to generate a plane using GL_TRIANGLES?

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!

Create AABB on 3D models in OpenGL

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 );
}
}

Normals for height map data

I want to find normals for height map data. I am using gl_triangles in my code for indices. How would I find normals for this?
Given a triangle (vert1, vert2, vert3) its normal is ((vert2 - vert1).cross(vert3 - vert1)).normalize().
For smooth, per-vertex normals: Foreach vertex, sum together the face normals for each triangle that vertex is a part of, then normalize the sum.
EDIT: Example:
#include <GL/glut.h>
#include <vector>
#include <cmath>
#include <Eigen/Core>
#include <Eigen/Geometry>
using namespace std;
using namespace Eigen;
typedef Matrix< Vector3f, Dynamic, Dynamic > VecMat;
// given a matrix of heights returns a matrix of vertices
VecMat GetVerts( const MatrixXf& hm )
{
VecMat verts( hm.rows(), hm.cols() );
for( int col = 0; col < hm.cols(); ++col )
for( int row = 0; row < hm.rows(); ++row )
verts( row, col ) = Vector3f( col, row, hm( row, col ) );
return verts;
}
VecMat GetNormals( const VecMat& hm )
{
VecMat normals( hm );
for( int col = 0; col < hm.cols(); ++col )
for( int row = 0; row < hm.rows(); ++row )
{
Vector3f sum( Vector3f::Zero() );
const Vector3f& cur = hm( row, col );
if( row+1 < hm.rows() && col+1 < hm.cols() )
sum += ( hm( row+0, col+1 ) - cur ).cross( hm( row+1, col+0 ) - cur ).normalized();
if( row+1 < hm.rows() && col > 0 )
sum += ( hm( row+1, col+0 ) - cur ).cross( hm( row+0, col-1 ) - cur ).normalized();
if( row > 0 && col > 0 )
sum += ( hm( row+0, col-1 ) - cur ).cross( hm( row-1, col+0 ) - cur ).normalized();
if( row > 0 && col+1 < hm.cols() )
sum += ( hm( row-1, col+0 ) - cur ).cross( hm( row+0, col+1 ) - cur ).normalized();
normals( row, col ) = sum.normalized();
}
return normals;
}
// returns an index array for a GL_TRIANGLES heightmap
vector< unsigned int > GetIndices( int rows, int cols )
{
vector< unsigned int > indices;
for( int col = 1; col < cols; ++col )
for( int row = 1; row < rows; ++row )
{
// Eigen default storage order is column-major
// lower triangle
indices.push_back( (col-1) * rows + (row-1) );
indices.push_back( (col-0) * rows + (row-1) );
indices.push_back( (col-1) * rows + (row-0) );
// upper triangle
indices.push_back( (col-1) * rows + (row-0) );
indices.push_back( (col-0) * rows + (row-1) );
indices.push_back( (col-0) * rows + (row-0) );
}
return indices;
}
VecMat heightmap;
VecMat normals;
vector< unsigned int > indices;
void init()
{
// wavy heightmap
MatrixXf hm( 64, 64 );
for( int col = 1; col < hm.cols(); ++col )
for( int row = 1; row < hm.rows(); ++row )
{
float x = ( col - ( hm.cols() / 2.0f ) ) / 2.0f;
float y = ( row - ( hm.rows() / 2.0f ) ) / 2.0f;
hm( row, col ) = cos( sqrt( x * x + y * y ) );
}
heightmap = GetVerts( hm );
heightmap.array() -= Vector3f( hm.cols() / 2.0f, hm.rows() / 2.0f, 0 );
for( int col = 0; col < hm.cols(); ++col )
for( int row = 0; row < hm.rows(); ++row )
heightmap( row, col ).array() *= Vector3f( 1 / 4.0f, 1 / 4.0f, 1.0f ).array();
normals = GetNormals( heightmap );
indices = GetIndices( heightmap.rows(), heightmap.cols() );
}
void display()
{
glEnable( GL_DEPTH_TEST );
glEnable( GL_CULL_FACE );
glShadeModel( GL_SMOOTH );
glEnable( GL_LIGHTING );
GLfloat global_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, global_ambient );
glEnable( GL_COLOR_MATERIAL );
glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
gluPerspective( 60, w / h, 1, 100 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt( 8, 8, 8, 0, 0, 0, 0, 0, 1 );
// spinning light
glEnable( GL_LIGHT0 );
float angle = 20 * ( glutGet( GLUT_ELAPSED_TIME ) / 1000.0f ) * (3.14159f / 180.0f);
float x = cos( -angle ) * 6;
float y = sin( -angle ) * 6;
GLfloat light_position[] = { x, y, 2, 1.0 };
glLightfv( GL_LIGHT0, GL_POSITION, light_position );
glDisable( GL_LIGHTING );
glPointSize( 5 );
glBegin(GL_POINTS);
glColor3ub( 255, 255, 255 );
glVertex3fv( light_position );
glEnd();
glEnable( GL_LIGHTING );
glColor3ub(255,0,0);
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
glVertexPointer( 3, GL_FLOAT, sizeof( Vector3f ), heightmap(0,0).data() );
glNormalPointer( GL_FLOAT, sizeof( Vector3f ), normals(0,0).data() );
glDrawElements( GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, &indices[0] );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
glutSwapBuffers();
}
void timer( int extra )
{
glutPostRedisplay();
glutTimerFunc( 16, timer, 0 );
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "Heightmap" );
init();
glutDisplayFunc( display );
glutTimerFunc( 0, timer, 0 );
glutMainLoop();
return 0;
}