Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I am trying make an OBJ loder program, but no matter how hard I try, it simply just does not work properly. There are several faces missing. I turned on the triangulate faces, and the Keep vertex order in Blender, but I can not make it work
.
Here is my OBJLoader code:
#include "OBJLoader.h"
void OBJLoader::Load(string file){
int lines = 0;
int indexsize = 0;
int offset = 0;
vector<string> datalines;
vector<vec3> position;
vector<vec3> normal;
vector<vec2> texturecoord;
bool progress[] = { false, false, false, false};
bool succes = IOManager::readFiletoLines(file, datalines, lines);
if (!succes){
char* tmp = "";
cin >> tmp;
exit(-1);
}
int facecount = 0;
string::size_type sz;
for (int i = 0; i < lines-1; i++){
string line = datalines[i];
if (line.at(0) == 'v'){
if (line.at(1) == ' '){
if (!progress[0]){
cout << "Loading positions..." << endl;
progress[0] = true;
}
offset = 2;
float x = stof(line.substr(offset), &sz);
offset += sz+1;
float y = stof(line.substr(offset), &sz);
offset += sz+1;
float z = stof(line.substr(offset), &sz);
position.push_back(vec3(x, y, z));
continue;
}
else if (line.at(1) == 't'){
if (!progress[1]){
cout << "Loading texture coordinates..." << endl;
progress[1] = true;
}
offset = 3;
float u = stof(line.substr(offset), &sz);
offset += sz+1;
float v = stof(line.substr(offset), &sz);
texturecoord.push_back(vec2(u, v));
continue;
}else if (line.at(1) == 'n'){
if (!progress[2]){
cout << "Loading normals..." << endl;
progress[2] = true;
}
offset = 3;
float x = stof(line.substr(offset), &sz);
offset += sz+1;
float y = stof(line.substr(offset), &sz);
offset += sz+1;
float z = stof(line.substr(offset), &sz);
normal.push_back(vec3(x, y, z));
cout << "a";
continue;
}
}else if (line.at(0) == 'f'){
if (!progress[3]){
cout << "Loading faces..." << endl;
data.resize(position.size());
progress[3] = true;
}
facecount++;
cout << facecount << endl;
Vertex vertex;
Vertex vertex2;
Vertex vertex3;
//First corner
offset = 2;
int pos1=stoi(line.substr(offset), &sz);
offset += sz+1;
int tex1=stoi(line.substr(offset), &sz);
offset += sz+1;
int norm1=stoi(line.substr(offset), &sz);
//Second corner
offset += sz+1;
int pos2 = stoi(line.substr(offset), &sz);
offset += sz + 1;
int tex2 = stoi(line.substr(offset), &sz);
offset += sz + 1;
int norm2 = stoi(line.substr(offset), &sz);
//Third corner
offset += sz+1;
int pos3 = stoi(line.substr(offset), &sz);
offset += sz + 1;
int tex3 = stoi(line.substr(offset), &sz);
offset += sz + 1;
int norm3 = stoi(line.substr(offset), &sz);
//put the values into the data vector
vertex.postion(position[pos1 - 1].x, position[pos1 - 1].y, position[pos1 - 1].z);
vertex.textureUV(texturecoord[tex1 - 1].x, texturecoord[tex1 - 1].y);
vertex.normal(normal[norm1 - 1].x, normal[norm1 - 1].y, normal[norm1 - 1].z);
vertex.colorRGBA(1, 1, 1, 1);
data[pos1 - 1] = vertex;
vertex2.postion(position[pos2 - 1].x, position[pos2 - 1].y, position[pos2 - 1].z);
vertex2.textureUV(texturecoord[tex2 - 1].x, texturecoord[tex2 - 1].y);
vertex2.normal(normal[norm2 - 1].x, normal[norm2 - 1].y, normal[norm2 - 1].z);
vertex2.colorRGBA(1, 1, 1, 1);
data[pos2 - 1] = vertex2;
vertex3.postion(position[pos3 - 1].x, position[pos3 - 1].y, position[pos3 - 1].z);
vertex3.textureUV(texturecoord[tex3 - 1].x, texturecoord[tex3 - 1].y);
vertex3.normal(normal[norm3 - 1].x, normal[norm3 - 1].y, normal[norm3 - 1].z);
vertex3.colorRGBA(1, 1, 1, 1);
data[pos3 - 1] = vertex3;
indexdata.push_back(pos1-1);
indexdata.push_back(pos2-1);
indexdata.push_back(pos3-1);
}
}
}
And this is my rendering function.
void Loader::Model(char* model,int texture){
if (!models[0]){
openGL.Load(model);
data = openGL.data;
indexdata = openGL.indexdata;
models[0] = true;
}
initeverything();
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBufferData(GL_ARRAY_BUFFER, data.size()*sizeof(Vertex), &data.front() , GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.size()*sizeof(int), &indexdata.front(), GL_STATIC_DRAW);
cout << data[0].pos.x << endl;
glEnableVertexArrayAttrib(vao, 0);
glEnableVertexArrayAttrib(vao, 1);
glEnableVertexArrayAttrib(vao, 2);
glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (void*)offsetof(Vertex, pos));
glVertexAttribPointer(1, 4, GL_FLOAT, false, sizeof(Vertex), (void*)offsetof(Vertex, color));
glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof(Vertex), (void*)offsetof(Vertex, vertUV));
glDrawElementsBaseVertex(GL_TRIANGLES, openGL.indexdata.size()*sizeof(int), GL_UNSIGNED_INT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
And this is the result:
And it also happens on a simple cube.
Yes. That supposed to be a cube.
If I had to guess, I'd say the "Keep Vertex Order" flag might be the issue. OpenGL normally culls faces that aren't CounterClockWise oriented, and based on the triangles which got culled (which appear, on the surface, to be every-other-face for the affected objects), it's likely that those triangles are having their vertices be drawn in ClockWise order.
You could, for simplicity's sake, simply tell OpenGL to not cull faces:
glDisable(GL_CULL_FACE);
But a better solution might be to make sure the winding order is consistent on all the faces of your object.
Related
I'm trying to center a square texture on a circle using openGL, I have created a loop that fills an array with my circle coordinates, followed by my +/-normals for lighting, finally followed by my (x,y) for the texture coordinates.
For some reason the image ends up kind of smooshed instead of evenly spreading out around the circle. I'm ot sure if my calculations for the polar to cartesian coordinates are a little off or what.
Below is a section of the code as well as an image of the output.
{
/*FIX STILL NEEDED TO DYNAMICALLY UPDATE THESE VALUES*/
//When changing numberOfSides need to also update indices[] to numberOfSides*3 and allCircleVertices[] to numberOfVertices*numberOfSides
GLfloat x = 0;
GLfloat y = 0;
GLfloat z = 0;
GLfloat radius = 4;
GLuint numberOfSides = 20;
GLuint numberOfVertices = numberOfSides + 1;
GLuint k = 22;
GLuint angle = 360 / numberOfSides;
GLushort indices[60];
GLfloat doublePi = 2.0f * M_PI;
GLfloat* circleVerticesX = new GLfloat[numberOfVertices];
GLfloat* circleVerticesY = new GLfloat[numberOfVertices];
GLfloat* circleVerticesZ = new GLfloat[numberOfVertices];
GLfloat allCircleVertices[420]; /*= new GLfloat[numberOfVertices * numberOfSides];*/
circleVerticesX[0] = x;
circleVerticesY[0] = y;
circleVerticesZ[0] = z;
//Loop to determine angles between vertices
for (int i = 1; i < numberOfVertices; i++)
{
circleVerticesX[i] = x + (radius * cos(i * doublePi / numberOfSides));
circleVerticesY[i] = y;
circleVerticesZ[i] = z + (radius * sin(i * doublePi / numberOfSides));
}
//Loop to fill array with vertices
for (int i = 0; i < numberOfVertices; i++)
{
allCircleVertices[i * 8] = circleVerticesX[i];
allCircleVertices[(i * 8) + 1] = circleVerticesY[i];
allCircleVertices[(i * 8) + 2] = circleVerticesZ[i];
allCircleVertices[(i * 8) + 3] = 0.0f;
allCircleVertices[(i * 8) + 4] = 1.0f;
allCircleVertices[(i * 8) + 5] = 0.0f;
if ((i * 8) + 6 == 6)
{
allCircleVertices[6] = 0.5f;
}
else
{
allCircleVertices[(i * 8 + 6)] = 0.5 * (sin(angle * i));
}
if ((i * 8 + 7) == 7)
{
allCircleVertices[7] = 0.5f;
}
else
{
allCircleVertices[(i * 8 + 7)] = 0.5 * (cos(angle * i));
}
}
//For loop to fill Indices array with correct indices based on number of sides
for (int i = 0; i < numberOfSides; i++)
{
if (i == (numberOfSides - 1))
{
indices[i * 3] = 0;
indices[(i * 3) + 1] = indices[2];
indices[(i * 3) + 2] = numberOfSides;
//cout << indices[i * 3] << ", " << indices[(i * 3) + 1] << ", " << indices[(i * 3) + 2] << endl;
}
else
{
indices[i * 3] = 0;
indices[(i * 3) + 1] = i + 2;
indices[(i * 3) + 2] = i + 1;
//cout << indices[i * 3] << ", " << indices[(i * 3) + 1] << ", " << indices[(i * 3) + 2] << endl;
}
}
const GLuint floatsPerVertex = 3;
const GLuint floatsPerNormal = 3;
const GLuint floatsPerUV = 2;
glGenVertexArrays(1, &mesh.vao); // we can also generate multiple VAOs or buffers at the same time
glBindVertexArray(mesh.vao);
// Create 2 buffers: first one for the vertex data; second one for the indices
glGenBuffers(2, mesh.vbos);
glBindBuffer(GL_ARRAY_BUFFER, mesh.vbos[0]); // Activates the buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(allCircleVertices), allCircleVertices, GL_STATIC_DRAW); // Sends vertex or coordinate data to the GPU
mesh.nIndices = sizeof(indices) / sizeof(indices[0]) * (floatsPerVertex + floatsPerNormal + floatsPerUV);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.vbos[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Strides between vertex coordinates is 6 (x, y, z, r, g, b, a). A tightly packed stride is 0.
GLint stride = sizeof(float) * (floatsPerVertex + floatsPerNormal + floatsPerUV);// The number of floats before each
// Create Vertex Attribute Pointers
glVertexAttribPointer(0, floatsPerVertex, GL_FLOAT, GL_FALSE, stride, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, floatsPerNormal, GL_FLOAT, GL_FALSE, stride, (char*)(sizeof(float)* floatsPerVertex));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, floatsPerUV, GL_FLOAT, GL_FALSE, stride, (void*)(sizeof(float)* (floatsPerVertex + floatsPerNormal)));
glEnableVertexAttribArray(2);
}
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.
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!
I'm not sure if this belongs here or in graphichs progamming.....I am getting a really annoying access violation reading error and I can't figure out why. What I am trying to do is to refactor a keyframing function (calculate the mid position between two vertices positions). This function compiles and works fine
glBegin(GL_TRIANGLES);
for(int i = 0; i < numTriangles; i++) {
MD2Triangle* triangle = triangles + i;
for(int j = 0; j < 3; j++) {
MD2Vertex* v1 = frame1->vertices + triangle->vertices[j];
MD2Vertex* v2 = frame2->vertices + triangle->vertices[j];
Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
Vec3f normal = v1->normal * (1 - frac) + v2->normal * frac;
if (normal[0] == 0 && normal[1] == 0 && normal[2] == 0) {
normal = Vec3f(0, 0, 1);
}
glNormal3f(normal[0], normal[1], normal[2]);
MD2TexCoord* texCoord = texCoords + triangle->texCoords[j];
glTexCoord2f(texCoord->texCoordX, texCoord->texCoordY);
glVertex3f(pos[0], pos[1], pos[2]);
}
}
glEnd();
Here the function calculates the positions and draws them. What I'd like to do is to calculate all positions before hand, store them in a Vertex array and then draw them.
If I try and remove it and replace this bloc in the exact same part of the program with the following
int vCount = 0;
for(int i = 0; i < numTriangles; i++) {
MD2Triangle* triangle = triangles + i;
for(int j = 0; j < 3; j++) {
MD2Vertex* v1 = frame1->vertices + triangle->vertices[j];
MD2Vertex* v2 = frame2->vertices + triangle->vertices[j];
Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
Vec3f normal = v1->normal * (1 - frac) + v2->normal * frac;
if (normal[0] == 0 && normal[1] == 0 && normal[2] == 0) {
normal = Vec3f(0, 0, 1);
}
indices[vCount] = normal[0];
vCount++;
indices[vCount] = normal[1];
vCount++;
indices[vCount] = normal[2];
vCount++;
MD2TexCoord* texCoord = texCoords + triangle->texCoords[j];
indices[vCount] = texCoord->texCoordX;
vCount++;
indices[vCount] = texCoord->texCoordY;
vCount++;
indices[vCount] = pos[0];
vCount++;
indices[vCount] = pos[1];
vCount++;
indices[vCount] = pos[2];
vCount++;
}
}
I get access violation error "Unhandled exception at 0x01455626 in Graphics_template_1.exe: 0xC0000005: Access violation reading location 0xed5243c0" pointing at line 7
Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
where the two Vs seems to have no value in the debugger.... Till this point the function behaves in exactly the same way as the one above, I don't understand why this happens?
EDIT ------------------------------------------------------------------------------------
Thank you Werner for spotting that the issue was the array initialization! As per your advice I refactored the function using std:vector containers and made the drawing use glDrawArrays instead of immediate mode.... But instead of an improvement in performance the framerate is a lot lower than before! Am I using this function right/efficiently? This is the refactored draw function:
for(int i = 0; i < numTriangles; i++) {
MD2Triangle* triangle = triangles + i;
for(int j = 0; j < 3; j++) {
MD2Vertex* v1 = frame1->vertices + triangle->vertices[j];
MD2Vertex* v2 = frame2->vertices + triangle->vertices[j];
Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
Vec3f normal = v1->normal * (1 - frac) + v2->normal * frac;
if (normal[0] == 0 && normal[1] == 0 && normal[2] == 0) {
normal = Vec3f(0, 0, 1);
}
normals.push_back(normal[0]);
normals.push_back(normal[1]);
normals.push_back(normal[2]);
MD2TexCoord* texCoord = texCoords + triangle->texCoords[j];
textCoords.push_back(texCoord->texCoordX);
textCoords.push_back(texCoord->texCoordY);
vertices.push_back(pos[0]);
vertices.push_back(pos[1]);
vertices.push_back(pos[2]);
}
}
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glNormalPointer(GL_FLOAT, 0, &normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &textCoords[0]);
glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
glDrawArrays(GL_TRIANGLES, 0, vertices.size()/3);
glDisableClientState(GL_VERTEX_ARRAY); // disable vertex arrays
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
vertices.clear();
textCoords.clear();
normals.clear();
Is there something extra that I am doing here? Cos this is really meant to be more efficient that glBegin()/End(), right?
Thanks for your time and help!
OK, let's make it an answer. The guess was:
Does it crash when you do not write to indices? Did you check that you
are not writing past the end of indices (overwriting your other data
structs)?
Your reply wass, that you create an array GLfloat indices[] since you do not know the array size in advance.
The best (performing) solution would be to calculate the array size in advance and create the array approriately.
GLfloat *indices = new GLfloat[calculated_number_of_elements];
...use array...;
delete [] indices;
Better yet you can create a std::vector:
std::vector<GLfloat> indices(calculated_number_of_elements);
The vector also has the advantage that it can be resized dynamically.