• OpenGL Model Viewer
    13 replies, posted
So I have to make a program that reads in a .obj file and renders it in 3 different ways (you can change rendermodes by pressing S) - smooth, wireframe, and smooth+wireframe. I also have to implement picking in wireframe mode (you can click a point on the model and it outputs the info for that vertex) as well as trackball style model rotating and zoom in/out funcitonality. Right now i'm trying to sort the rendering part. Rendering in smooth mode works fine, and wireframe works fine too.. But my implementation of combined mode is incorrect i think - it doesn't instantly render, but rather it renders smooth first then there's a short wait period before it renders the wireframe overtop of it. The wireframe also looks like ass. [img]http://i.imgur.com/5kuTj1n.png[/img] Here's the relevant code. Once i sort this out, i'll be able to focus on making my picking code work properly. [code] void renderModel() { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glLoadName(15); glTranslatef(0, 0, -3); if (currentRenderMode == WIREFRAME) { glScalef(1.001, 1.001, 0.0); } // draw object glBegin(GL_TRIANGLES); { for (int i = 0; i<mesh->nfaces.size(); i += 1) { for (int j = 0; j<3; j += 1) { glNormal3f(mesh->normal[mesh->nfaces[i][j]][0], mesh->normal[mesh->nfaces[i][j]][1], mesh->normal[mesh->nfaces[i][j]][2]); glVertex3f(mesh->vertex[mesh->faces[i][j]][0], mesh->vertex[mesh->faces[i][j]][1], mesh->vertex[mesh->faces[i][j]][2]); } } } glEnd(); glutSwapBuffers(); } void preRender() { // Background color glClearColor(0.5, 0.5, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); // Matrix setup glMatrixMode(GL_PROJECTION); glViewport(0, 0, width, height); glLoadIdentity(); gluPerspective(20, (float)width / (float)height, 0.1, 1000); setupLights(); } // This function is called to display the scene. void display() { preRender(); if (currentRenderMode == WIREFRAME || currentRenderMode == COMPLETE) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); } else { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } renderModel(); if (currentRenderMode == COMPLETE) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); renderModel(); } }[/code] Any tips would be appreciated.
Try disabling depth testing while rendering the wireframe: [code]glDisable(GL_DEPTH_TEST);[/code] (you'll then have to add a glEnable call before your smooth rendering: enable -> smooth -> disable -> wireframe.) This will stop the "z-fighting" which occurs when two things are drawn at (nearly) the same depth, which is what's making your wireframe look shitty. It will also have the effect that you'll be able to see the back of the wireframe through the model, in a kind of x-ray style. Personally I think that would be pretty cool, but if you don't want that you could either use some sort of shader (probably not best because you're working in immediate mode right now) or simply offset the wireframe mesh along the normals by a small amount. (This is normally best done in a vertex shader, but you can probably get away with having two copies of the mesh, one regular for polygon rendering, and one displaced for wireframe.) [editline]blah[/editline] Or just amend your code for editing wireframe to fudge the vertices on the fly. As for your performance problems, graphics card line drawing is notoriously slow compared to triangles because they're designed for triangles, no idea if that's the root of your problems though. [editline]blah[/editline] For picking just project all your points into screen space first and then find the closest point to the mouse position.
So i did this: [code] preRender(); if (currentRenderMode == COMPLETE) { glEnable(GL_DEPTH_TEST); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); renderModel(); } if (currentRenderMode == WIREFRAME || currentRenderMode == COMPLETE) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glDisable(GL_DEPTH_TEST); } else { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_DEPTH_TEST); } renderModel();[/code] Now in complete mode, it renders the smooth part first, then when it renders wireframe over top, the smooth part becomes wireframe too... I think i am doing too much in renderModel(), is there something being called inside that function that should not be called more than once? [editline]29th January 2014[/editline] e: I got it working. I had to move glutSwapBuffers() out of the display function. The wireframe still looks crappy. I even scaled up the wireframe by 1.001 so it would render on top but no dice. [img]http://i.imgur.com/5aFxC35.png[/img] So there's that issue, plus my picking code doesn't seem to work at all. This was code posted by the teacher's aide so i don't know why it's not working for me. [code] void mouse_button(int button, int state, int x, int y) { int hits = 0; if (state == GLUT_DOWN) { glRenderMode(GL_SELECT); renderModel(); hits = glRenderMode(GL_RENDER); #ifdef BUG_KLUDGE if( hits == 0 ) { glRenderMode(GL_SELECT); renderModel(); hits = glRenderMode(GL_RENDER); } #endif if (true) { fprintf( stderr, "# pick hits = %d\n", hits ); } for (int i = 0, index = 0; i < hits; i++) { int items = PickBuffer[index++]; int zmin = PickBuffer[index++]; int zmax = PickBuffer[index++]; if (true) { fprintf(stderr, "Hit # %2d: found %2d items on the name stack\n", i, items); fprintf(stderr, "\tZmin = 0x%0x, Zmax = 0x%0x\n", zmin, zmax); } for (int j = 0; j < items; j++) { int item = PickBuffer[index++]; // do something with item ; if (true) { fprintf( stderr, "\t%2d: %6d\n", j, item ); } } } glutPostRedisplay(); } if (hits == 0) { /* didn’t pick anything */ /* use the left mouse for rotation or scaling: */ } }[/code]
Ok so i have made lots of progress on the render modes part. Fixed the z-fighting issues! [img]http://i.imgur.com/mUadEGn.png[/img] Now i am trying to implement the arcball camera controls, my prof linked this article on the assignment page: [url]http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Arcball[/url] So basically i just have to convert that to C++, which up to now has been super easy, except i have to convert this line: [code]camera2object = glm::inverse(glm::mat3(transforms[MODE_CAMERA]) * glm::mat3(mesh.object2world));[/code] camera2object is a vector. Conveniently, my prof wrote vector and matrix classes for us to use in this assignment. I just have no idea what glm::inverse really does, and there's nothing in his vector/matrix classes that mentions it. I can replace glm::mat3 with his matrix class name, but i don't know what transforms[MODE_CAMERA] represents. I also don't get what mesh.object2world signifies.
Whoa, looks much better! You might want to read [url=http://open.gl/transformations]this page[/url] about matrix transformations in OpenGL first. Then, the formal definition of the matrix inverse is this: [quote=Wikipedia]A square matrix [B]A[/B] is called invertible or non-singular if there exists a matrix [B]B[/B] such that [B]AB[/B] = [B]BA[/B] = [B]I[/B]n. If [B]B[/B] exists, it is unique and is called the inverse matrix of [B]A[/B], denoted [B]A[/B]^&#8722;1[/quote] In human terms, the inverse matrix is the matrix such that applying it after applying the original matrix gets you back to where you started. The mathematical process behind it is fairly complex and abstract, but intuitively it gives you the "opposite" of whatever transformation is represented by your original matrix. So that code snippet above composes the camera matrix (which will transform from world space to camera space), with the matrix called mesh.object2world, which transforms from object (local) coordinates to world coordinates. Transformations are applied right-to-left, so this will transform local object coords -> world coords -> camera space coords. You then take the inverse of this matrix, i.e. the opposite. The way this works under the hood is completely numeric in nature, but what it does is create the matrix that transforms from camera space -> local object space (i.e. the coordinates specified by your model). Magic!
Oh i'm a fool, that example assumes that the model is not centered already so i can omit that part in my code. I'm still having issues with the arcball code, when i drag my mouse nothing happens, but if i move it to a very specific spot in the bottom left, the model turns upside down and tilts left. Either I have interpreted the code wrong or i'm not using glRotatef properly [code] void idle() { if (cur_mx != last_mx || cur_my != last_my) { TVector va = arcVector(last_mx, last_my); TVector vb = arcVector(cur_mx, cur_my); cameraAng = acos(min(1.0f, va | vb)); // va dot vb cameraVec = va * vb; // va cross vb cout << cameraAng << " " << cameraVec[0] << " " << cameraVec[1] << " " << cameraVec[2] << " " << endl; //TVector camera2object = glm::inverse(glm::mat3(transforms[MODE_CAMERA]) * glm::mat3(mesh.object2world)); //TVector axis_in_object_coord = camera2object * axis_in_camera_coord; //mesh.object2world = glm::rotate(mesh.object2world, glm::degrees(angle), axis_in_object_coord); last_mx = cur_mx; last_my = cur_my; } } TVector arcVector(int x, int y) { TVector p = TVector(x / width * 2 - 1.0, (y / height * 2 - 1.0) * -1, 0); float squared = p[0] * p[0] + p[1] * p[1]; if (squared <= 1) { p[2] = sqrt(1 - squared); } else { p.normalize(); // nearest point } return p; } // This is taken from the render code glRotatef(cameraAng * 360, cameraVec[0], cameraVec[1], cameraVec[2]);[/code] This is the output from when i drag my mouse, something is messed up because those numbers should be changing every frame [img]http://i.imgur.com/3HfaeIY.png[/img]
your axis are wrong... you rotate around the axis 0,0,0 with 0.00345... *360 degree if your values would be lets say 0,0,1.f and 45 you would rotate around z axis with 45 °
I have a feeling i misinerpreted this shitty ass tutorial. [code] glm::vec3 get_arcball_vector(int x, int y) { glm::vec3 P = glm::vec3(1.0*x/screen_width*2 - 1.0, 1.0*y/screen_height*2 - 1.0, 0); P.y = -P.y; float OP_squared = P.x * P.x + P.y * P.y; if (OP_squared <= 1*1) P.z = sqrt(1*1 - OP_squared); // Pythagore else P = glm::normalize(P); // nearest point return P; }[/code] Why would you do sqrt(1*1 - OP_squared)??? Wouldn't 1*1 get evaluated first? Why multiply anything by 1? The same thing happens on the second line.
It's just to explicitly show that you're using the [url=http://en.wikipedia.org/wiki/Pythagorean_theorem]Pythagorean theorem[/url], as if the comment wasn't enough. The compiler should optimize it out, so it shouldn't have any impact on performance.
I made some major progress, the arcball interface works completely now and picking is nearly complete.
Alright so picking works but i need to implement vertex manipulation... Specifically, i need to be able to click a vertex then move it in a direction parallel to the viewport. My current implementation doesn't move them parallel to the viewport. Would this calculation be similar to my arcball calculations? [code] if (cur_mx != last_mx || cur_my != last_my) { TVector va = arcVector(last_mx, last_my); TVector vb = arcVector(cur_mx, cur_my); cameraAng = acos(min(1.0f, va | vb)) * (180 / 3.141592654); cameraVec = va * vb; glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glRotatef(cameraAng * 3, cameraVec[0], cameraVec[1], cameraVec[2]); GLfloat model[16]; TMatrix4x4 mat; glGetFloatv(GL_MODELVIEW_MATRIX, model); for (int i=0; i<16; ++i) { mat[i] = model[i]; } trackballMat = mat * trackballMat; glPopMatrix(); last_mx = cur_mx; last_my = cur_my; }[/code] Right now i'm doing some dumb hacky crap to move my vertices and it's all wrong. [code] if (manipulating) { TVector va = arcVector(manip_mx, manip_my); TVector vb = arcVector(mx, my); TVector vec = va * vb; if (cameraVec[0] < 0) manip_dx = -vec[1] * 0.1; else manip_dx = vec[1] * 0.1; if (cameraVec[1] < 0) manip_dy = vec[0] * 0.1; else manip_dy = -vec[0] * 0.1; manip_mx = mx; manip_my = my; glutPostRedisplay(); } // from draw code for (int i = 0; i < mesh->nfaces.size(); i += 1) { for (int j = 0; j < 3; j += 1) { glNormal3f(mesh->normal[mesh->nfaces[i][j]][0], mesh->normal[mesh->nfaces[i][j]][1], mesh->normal[mesh->nfaces[i][j]][2]); if (manipulating && currentVert == mesh->faces[i][j]) { mesh->vertex[currentVert][0] = mesh->vertex[currentVert][0] + manip_dx; mesh->vertex[currentVert][1] = mesh->vertex[currentVert][1] + manip_dy; } glVertex3f(mesh->vertex[mesh->faces[i][j]][0], mesh->vertex[mesh->faces[i][j]][1], mesh->vertex[mesh->faces[i][j]][2]); } }[/code]
if you have a camera matrix or final model view matrix that is orthogonal its pretty easy to move something parallel to the view! the definition of the matrix is actually a set of 3 directions and position. the three directions are x, y and z axis (the rotation) :) But you might need the inverse matrix ... depends on how your vertices are stored... (i am bad at math, so don't take my answer too serious xD)
Unfortunately it was not orthogonal but i did end up figuring it out. I am working on implementing animations and linear blend skinning but it's a fucking pain in the ass. I have the bones animated properly but now i need to make the mesh follow it. I don't know how i'm supposed to do this properly. [code] void applyAnimTransforms(int index) { Weight weight = getMeshTransform(index); TVector vec; vec[0] = 0; vec[1] = 0; vec[2] = 0; for (int j = 0; j < weight.values.size(); j++) { double boneWeight = weight.values.at(j); Transform trans = getTransform(bones[j].id); trans.translation = trans.translation * boneWeight; vec = vec + trans.translation; } glTranslatef(vec[0], vec[1], vec[2]); } // from draw code glBegin(GL_TRIANGLES); for (int i = 0; i < mesh->nfaces.size(); i += 1) { for (int j = 0; j < 3; j += 1) { applyAnimTransforms(mesh->faces[i][j]); glNormal3f(mesh->normal[mesh->nfaces[i][j]][0], mesh->normal[mesh->nfaces[i][j]][1], mesh->normal[mesh->nfaces[i][j]][2]); glVertex3f(mesh->vertex[mesh->faces[i][j]][0], mesh->vertex[mesh->faces[i][j]][1], mesh->vertex[mesh->faces[i][j]][2]); } } glEnd();[/code] This seems to make it really laggy and does not do anything since you have to call glTranslate before glBegin or else it does nothing. So i have to move the glBegin inside of the nested loops i guess?
This is how your programm should work: [CODE] void mainloop{ while(running){ work(); draw(); } } [/CODE] so you have just to do all the work you need to do, e.g. animate, translate, rotate, and so on (game related calculate physics/ ai) and then just render the result (draw).
Sorry, you need to Log In to post a reply to this thread.