1

I've taken it upon myself to add OBJ parser/importer support to a 3D rendering engine I've been working on. I've followed the specification found HERE nearly 'to a tee', with the current exception of limiting all support to groups, faces, vertices, normals and texture coordinates (so no material library or free-form poly support, as of yet). My goal was to simply parse line by line -- generating an object-oriented, hierarchical tree-like scene graph as I went along -- and allow the developer to automatically bind the data to a shader program with very few manual calls in order to start manipulating & viewing the mesh. The end result is that my engine does successfully parse most (if not all) valid OBJ format files, extracting the appropriate data and sending it to a basic shader for rendering. However, even though the data appears to be represented correctly in the scene graph, for some reason or another it rarely renders properly...

Take note that a simple plane (exported from 3DS Max, containing only 4 vertices and 2 faces) renders perfectly fine, but a cube or anything more advanced usually ends up looking something like this:

http://youtu.be/1x6bnuhAXWY

I can't tell where things are going wrong, and AFAIK my code should actually be parsing and rendering basic geometry just fine... So why isn't it? For convenience, I have uploaded my project HERE. It contains a NetBeans project with a minimal version of my engine, and one Test application. I have also included 3 different versions of an OBJ cube mesh, and a single plane mesh. The application is configurable by editing the values at the top of Test.java, and the only input controls are A, S, W, & D for mesh translation, and mouse movement for mesh rotation. And although I've managed to skim the project down considerably, the most notable classes include extra comments/info at the top of the file.

All things considered, I'll take whatever thoughts I can get... and it certainly won't go unappreciated!

RectangleEquals
  • 1,825
  • 2
  • 25
  • 44

1 Answers1

6

I haven't downloaded your project. What people mostly struggle with when writing OBJ import code for rendering with OpenGL is the indices. And as @ratched_freak also suspects in his comment, that's very consistent with the visual appearance of your cube.

The OBJ format uses separate indices for positions, normals, and texture coordinates. For OpenGL rendering, you need a single set of indices. This means that you need to generate a vertex for each unique combination of position/normal/texture indices used by triangles in the OBJ file, assign a new index to the combination, and then use that index in your OpenGL index buffer.

I wrote an answer with pseudo-code that outlines how to do this for a similar question recently: OpenGL - Index buffers difficulties.

Edit, to illustrate the problem some more. Here is a "cube" file I found online:

v  0.0  0.0  0.0
v  0.0  0.0  1.0
v  0.0  1.0  0.0
v  0.0  1.0  1.0
v  1.0  0.0  0.0
v  1.0  0.0  1.0
v  1.0  1.0  0.0
v  1.0  1.0  1.0

vn  0.0  0.0  1.0
vn  0.0  0.0 -1.0
vn  0.0  1.0  0.0
vn  0.0 -1.0  0.0
vn  1.0  0.0  0.0
vn -1.0  0.0  0.0

f  1//2  7//2  5//2
f  1//2  3//2  7//2 
f  1//6  4//6  3//6 
f  1//6  2//6  4//6 
f  3//3  8//3  7//3 
f  3//3  4//3  8//3 
f  5//5  7//5  8//5 
f  5//5  8//5  6//5 
f  1//4  5//4  6//4 
f  1//4  6//4  2//4 
f  2//1  6//1  8//1 
f  2//1  8//1  4//1 

The file has 8 positions (v records) and 6 normal (vn records). The f records are faces, triangles in this case. Looking at the first triangle vertex, 1//2 tells you that the vertex uses position 1 and normal 2. When using OpenGL index arrays, you can't have separate indices for positions and normals. So we create a vertex for this position/normal pair, and assign it the first available index. Same for 7//2 and 5//2, so we now have 3 OpenGL vertices (indices 0, 1, and 2).

Now on the second triangle, we find 1//2 again. We already created a vertex for this combination, so we can use our vertex 0 again. 3//2 is new, so we create a new vertex (index 3) for it. 7//2 we saw previously, it's the same as our vertex 1.

So we ended up with 4 OpenGL vertices for the fist 2 triangles. Which makes sense, since the two triangles describe one face of the cube, and we need 4 vertices for a square.

If you continue this process for the whole example, you will end up with 24 vertices you can store in an OpenGL vertex buffer, and an index buffer with 36 entries (12 triangles with 3 corners each).

Community
  • 1
  • 1
Reto Koradi
  • 53,228
  • 8
  • 93
  • 133
  • 1
    So you're telling me that OpenGL takes indices differently than what an OBJ file contains, and that I would have to remap them accordingly? Is there a more uniform way of going about this? I only ask because a simple plane object appears to render just fine, but all 3 versions of the cube do not... So how would I know what & when to remap these indices? – RectangleEquals May 18 '14 at 04:38
  • Furthermore, the pseudo-code in the link you've provided isn't entirely clear to me, and appears to be geared more for C++ devs. There is no STL in Java, so it would need to be implemented differently. – RectangleEquals May 18 '14 at 04:51
  • I might have used some method names suggesting a C++ map, but as long as you have similar containers in your preferred language, it should translate easily. In Java, you can use a `java.util.Map`, which is very similar. In Python, it would be a `dict`. – Reto Koradi May 18 '14 at 05:03
  • 2
    Right on the indices. Not sure what you mean by "more uniform", though. This is all very systematic. There is an easy way out if you don't care much about memory usage and performance. It's the solution the poster of the other question describes in the other answer. But what I outlined really doesn't take more than a few lines of code to remap the indices. I'll explain the problem some more in the answer. – Reto Koradi May 18 '14 at 05:10
  • Actually, that cube renders just fine in my engine. And the issue here has nothing to do with the mesh's normals, because my shader only takes in vertices, indices, vertex colors, and texture coordinates (which are currently not being used anyhow), so the issue lies with the way the indices and/or vertices are laid out in separate OBJ files... Perhaps you should actually take a look at the OBJ files I've included in my project, so you can see the difference between your cube and the other 3 found there. – RectangleEquals May 18 '14 at 07:02
  • And if you got a minute, the import code is worth a look. All together, it's less than 1k lines of code separated into 24 small classes, so it's rather neat, organized & relatively easy on the eyes. However, the only ones to be concerned with are the Parser, SceneGraph, VertexBuffer, and Test classes, which contain less than 450 lines of actual code all together. If it were any less, I would've just pasted them in my question... – RectangleEquals May 18 '14 at 07:34