C++ atioglxx.pdb not loaded error glBufferData OpenGL - c++

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

Related

How to update buffer data in rendering loop?

so I am trying to render Koch's snowflake. whenever the user clicks the "w" button it should generate a new generation using this function:
void nextGen(int& gen, vector<KochLine>& v, GLFWwindow* window, float angle) {
gen++;
if(glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS && gen <= 6) {
vector<KochLine> nextGen;
int p = power(4, gen);
for (int j = 0; j < v.size(); j++) {
if (j < p) {
v[j].generate(nextGen, angle);
} else {
v[j].generate(nextGen, -angle);
}
}
v = nextGen;
}
}
can anyone tell me how to do that?
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include ".\classes\KochLine.h"
#include ".\classes\Shader.h"
#define PI 3.1415926538
using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
}
void closeWindow(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
}
int power(int a, int b) {
int ans = 1;
while(b > 0) {
int lb = (b & 1);
if(lb) {
ans *= a;
}
a *= a;
b = b >> 1;
}
return ans;
}
void nextGen(int& gen, vector<KochLine>& v, GLFWwindow* window, float angle) {
gen++;
if(glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS && gen <= 6) {
vector<KochLine> nextGen;
int p = power(4, gen);
for (int j = 0; j < v.size(); j++) {
if (j < p) {
v[j].generate(nextGen, angle);
} else {
v[j].generate(nextGen, -angle);
}
}
v = nextGen;
}
}
int main() {
int gen = 0, type = -1;
while(type != 1 && type != 2) {
cout << "Enter which type of koch snowflake you want\n";
cout << "[1]Koch's Snowflake\n";
cout << "[2]Anti-Koch's Snowflake\n";
cin >> type;
}
// koch curve stuff
Vertex v1{-0.75f, -0.75f};
Vertex v2{0.75f, -0.75f};
Vertex v3{0.0f, 0.75f};
KochLine e2{v1, v2};
KochLine e1{v1, v3};
KochLine e3{v2, v3};
vector<KochLine> v;
v.push_back(e1);
v.push_back(e2);
v.push_back(e3);
float angle = PI / 3.0f;
if(type == 2) {
angle *= -1;
}
/*for(int i = 0; i < gen; i++) {
vector<KochLine> nextGen;
int p = power(4, i);
for(int j = 0; j < v.size(); j++) {
if(j < p) {
v[j].generate(nextGen, angle);
} else {
v[j].generate(nextGen, -angle);
}
}
v = nextGen;
}*/
float* vertecies = (float*)malloc(v.size() * 6 * sizeof(float));
int p = 0;
for(int i = 0; i < v.size() * 6; i += 6) {
vertecies[i] = v[p].p1.x;
vertecies[i + 1] = v[p].p1.y;
vertecies[i + 2] = 0.0f;
vertecies[i + 3] = v[p].p2.x;
vertecies[i + 4] = v[p].p2.y;
vertecies[i + 5] = 0.0f;
p++;
}
// openGL stuff
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 600, "Cobweb_Diagram", NULL, NULL);
if (window == NULL) {
cout << "Failed to create GLFW window" << endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
cout << "Failed to initialize GLAD" << endl;
return -1;
}
Shader ourShader("./shaders/my_vertex.vs", "./shaders/my_fragment.fs");
int k = v.size();
unsigned int VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, k * 6 * sizeof(float), vertecies, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(0));
glEnableVertexAttribArray(0);
while (!glfwWindowShouldClose(window)) {
closeWindow(window);
nextGen(gen, v, window, angle);
float* vertecies = (float*)malloc(v.size() * 6 * sizeof(float));
int p = 0;
for (int i = 0; i < v.size() * 6; i += 6) {
vertecies[i] = v[p].p1.x;
vertecies[i + 1] = v[p].p1.y;
vertecies[i + 2] = 0.0f;
vertecies[i + 3] = v[p].p2.x;
vertecies[i + 4] = v[p].p2.y;
vertecies[i + 5] = 0.0f;
p++;
}
glBufferData(GL_ARRAY_BUFFER, k * 6 * sizeof(float), vertecies, GL_STATIC_DRAW);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(VAO);
ourShader.use();
glDrawArrays(GL_LINES, 0, v.size() * 2);
glBindVertexArray(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
Edit: I tried to run glBufferData in render loop but it doesn't work.

glBufferSubData calling multiple times overrides the first data

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.

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

OpenGL vertex array objects with tinyobjloader

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

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