Runtime loading of .OBJ meshes
Moderator: Moderators
Runtime loading of .OBJ meshes
Being inspired by Kjell's loader of ZModeler files from this topic, I created a simple OBJ (Wavefront) file loader. It loads just vertices and normals of triangulated meshes. Comparing to .z3d, .obj file format is relatively common and can be produced by many 3d modeling tools. The attached examples I produced/modified by my favorite Wings3D and normals were corrected in Meshlab.
The code is not probably optimized yet, but I plan to have a closer look at it as part of work on Toyland Shooter - I'll use obj loader for user-defined meshes.
BTW I plan to test obj loader also on Android where meshes will be loaded from e.g. /sdcard/Meshes directory. I hope it will work.
Any comments, suggestions, etc. are welcome.
The code is not probably optimized yet, but I plan to have a closer look at it as part of work on Toyland Shooter - I'll use obj loader for user-defined meshes.
BTW I plan to test obj loader also on Android where meshes will be loaded from e.g. /sdcard/Meshes directory. I hope it will work.
Any comments, suggestions, etc. are welcome.
- Attachments
-
- ObjLoader_001.zip
- Runtime .OBJ file loader
- (147.63 KiB) Downloaded 1101 times
OBJ loader has successfully been tested also on Android. It is relatively slow for larger meshes (2-10 seconds for faces > 1000); do you have the same experience, or your device is faster? To try it, just install OBJ Loader-debug.apk and move any number of .obj files to /sdcard/Meshes/ directory (you can use any .obj files with triangulated meshes). Mesh files should be named obj<num>.obj.
- Attachments
-
- ObjLoader_002.zip
- OBJ loader also for android
- (601.25 KiB) Downloaded 1073 times
Hi Rado,
Perhaps you can try copying the entire file into a buffer ( array ) first, and then parse the data in a single while-loop .. should speed things up. Creating the mesh using OpenGL calls ( glBufferData ) instead of using a Mesh component might help as well.
By the way, Z3D isn't a "real" format
K
Perhaps you can try copying the entire file into a buffer ( array ) first, and then parse the data in a single while-loop .. should speed things up. Creating the mesh using OpenGL calls ( glBufferData ) instead of using a Mesh component might help as well.
By the way, Z3D isn't a "real" format
K
Hmmm... I'm not sure about this, what's the difference between reading + parsing at the same time and reading + consequent parsing? Of course, the input file can be opened for shorter time, which is an advantage, but how this could make the reading time shorter? I can try it anyway.Kjell wrote:Perhaps you can try copying the entire file into a buffer ( array ) first, and then parse the data in a single while-loop .. should speed things up.
Also setting of buffer sizes depending on file size could help little bit.
This is good idea! I hope it will run on my GPU and OpenGL ES as well.Kjell wrote:Creating the mesh using OpenGL calls ( glBufferData ) instead of using a Mesh component might help as well.
Hi Rado,
K
Because when you use a ZExpression in a Repeat component all local variables are (re-)allocated each iteration ( afaik ).Rado1 wrote:I'm not sure about this, what's the difference between reading + parsing at the same time and reading + consequent parsing? Of course, the input file can be opened for shorter time, which is an advantage, but how this could make the reading time shorter? I can try it anyway.
HintKjell wrote:I hope it will run on my GPU and OpenGL ES as well.
K
Hi Kjell,
attached is my intermediate attempt to avoid mesh recalculation by utilizing vertex arrays. It is faster than the previous version, however, this is still not performance optimal. In another version I also used glInterleavedArrays, but it is not working on Android and surprisingly its performance was worse than non-interleaved arrays on my GPU; I thrown it away. Unfortunately, trials to use VBOs failed. For instance, what value to put to glVertexPointer's 4th parameter called pointer (ZGE allows to specify only DefineArray as value for xptr AFAIK)? Could you please help me to modify obj loader to use buffer objects, if possible? Thanks.
Rado1.
attached is my intermediate attempt to avoid mesh recalculation by utilizing vertex arrays. It is faster than the previous version, however, this is still not performance optimal. In another version I also used glInterleavedArrays, but it is not working on Android and surprisingly its performance was worse than non-interleaved arrays on my GPU; I thrown it away. Unfortunately, trials to use VBOs failed. For instance, what value to put to glVertexPointer's 4th parameter called pointer (ZGE allows to specify only DefineArray as value for xptr AFAIK)? Could you please help me to modify obj loader to use buffer objects, if possible? Thanks.
Rado1.
- Attachments
-
- obj_loader_003.zgeproj
- obj loader with vertex arrays
- (11.77 KiB) Downloaded 1034 times
Hi Rado,
K
Never use glInterleavedArrays. If you want to use interleaved buffers, do this client-side and set the location of the individual attributes using strides.Rado1 wrote:In another version I also used glInterleavedArrays, but it is not working on Android and surprisingly its performance was worse than non-interleaved arrays on my GPU
You can also use NULL The name of this parameter is slightly confusing though, since it's used as a relative offset, not a absolute address.Rado1 wrote:For instance, what value to put to glVertexPointer's 4th parameter called pointer (ZGE allows to specify only DefineArray as value for xptr AFAIK)?
I'll have a peek later today.Rado1 wrote:Could you please help me to modify obj loader to use buffer objects, if possible?
K
Kjell, is this usage of buffer objects correct? See the attachment. Are there some further optimizations possible? I tested it only on obj files from ObjLoader_002.zip, but performance seems comparable to usage of simple vertex arrays (obj_loader_003.zgeproj)
- Attachments
-
- obj_loader_005.zgeproj
- obj loader with buffer objects
- (13.01 KiB) Downloaded 1120 times
Hi Rado1,
By the way, your parser doesn't work properly with OBJ's exported from Softimage.
K
Looks fine yesRado1 wrote:Kjell, is this usage of buffer objects correct?
Rendering performance of VBO's ( especially non-interleaved ) will be similar to Vertex Arrays for ( relatively ) low-poly meshes. But .. I thought you were trying to optimize loading & parsing?Rado1 wrote:Are there some further optimizations possible? I tested it only on obj files from ObjLoader_002.zip, but performance seems comparable to usage of simple vertex arrays.
By the way, your parser doesn't work properly with OBJ's exported from Softimage.
Code: Select all
# XSI Wavefront OBJ Export v3.0
o cube
# Hierarchy (from self to top father)
g cube
#begin 8 vertices
v -1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v -1.000000 -1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
v 1.000000 1.000000 1.000000
#end 8 vertices
#begin 36 normals
vn 0.000000 0.000000 -1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn -1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000
vn 1.000000 0.000000 -0.000000
vn 1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000
vn 0.000000 1.000000 0.000000
vn -0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 -0.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn -1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 0.000000 1.000000
#end 36 vertex normals
#begin 12 faces
f 1//1 4//3 2//4
f 1//5 6//7 5//8
f 1//9 7//11 3//12
f 2//13 8//15 6//16
f 3//17 8//19 4//20
f 5//21 8//23 7//24
f 3//2 4//26 1//25
f 2//6 6//28 1//27
f 5//10 7//30 1//29
f 4//14 8//32 2//31
f 7//18 8//34 3//33
f 6//22 8//36 5//35
#end 12 faces
Yes parsing was optimized, now it is only one expression called once per file. By "performance" I meant FPS, not the loading time.Kjell wrote:But .. I thought you were trying to optimize loading & parsing?
I know that. The problem are points of faces for which vertex index is different than normal index. How is it possible to call glDrawElements (or another function) where vertex indices and normal indices are different and stored in different arrays (or one interleaved array)? I would like to avoid inserting of artificial vertex copies corresponding to normals, drawing triangle by triangle, direct accessing by glArrayElement, or so. The solution should be OpenGL 2.0-compatible to be testable on my ATI Radeon X1400. Any ideas? BTW slow ObjLoader_002 should be able to load also these obj files correctly.Kjell wrote:By the way, your parser doesn't work properly with OBJ's exported from Softimage.
Rado1.
Hi Rado,
K
Alright .. in that case glDrawArrays or glDrawElements shouldn't make much of a difference no. However, since buffers can be uploaded to the GPU, this approach enables you to re-use the vertex / normal Arrays, which makes loading multiple meshes allot easier.Rado1 wrote:Yes parsing was optimized, now it is only one expression called once per file. By "performance" I meant FPS, not the loading time.
Not possible. OpenGL requires a uniform vertex format per draw call.Rado1 wrote:How is it possible to call glDrawElements (or another function) where vertex indices and normal indices are different and stored in different arrays (or one interleaved array)? I would like to avoid inserting of artificial vertex copies corresponding to normals, drawing triangle by triangle, direct accessing by glArrayElement, or so.
K
Hi Kjell,
I also tried 2-step processing: buffering files into an array and consequent processing of that array. I tried string, integer and float arrays, but everything was slower, from 10% to 50%, than the direct file reading. Therefore, I removed file buffer.
See the attached update. Any comments are welcome.
Rado1.
I made a workaround for reused vertices or normals - extending the vertex/normal array + copying of vertices/normals to the positions where they are used by normals/vertices. This results in uniform (aligned) arrays of vertices and normals. So the Softimage's OBJ files of triangulated meshes can be loaded now correctly. Performance is just a little bit worse than without aligning vertex arrays.Kjell wrote:Not possible. OpenGL requires a uniform vertex format per draw call.
I also tried 2-step processing: buffering files into an array and consequent processing of that array. I tried string, integer and float arrays, but everything was slower, from 10% to 50%, than the direct file reading. Therefore, I removed file buffer.
See the attached update. Any comments are welcome.
Rado1.
- Attachments
-
- obj_loader_006.zgeproj
- obj loader aligning vertex arrays
- (16.05 KiB) Downloaded 1001 times
Hi,
after private discussion with Kjell I tried to make the obj loader even faster. All transformations of input chars to strings and consequent parsing of string tokens were removed. I think the only bottleneck of the current version is relatively slow loading of files in ZGE - FileMoveData component.
after private discussion with Kjell I tried to make the obj loader even faster. All transformations of input chars to strings and consequent parsing of string tokens were removed. I think the only bottleneck of the current version is relatively slow loading of files in ZGE - FileMoveData component.
- Attachments
-
- obj_loader_007.zgeproj
- faster obj loader
- (19.77 KiB) Downloaded 1124 times
Last edited by Rado1 on Sun Oct 28, 2012 9:43 pm, edited 1 time in total.
Really neat trick of calling the MoveData component inside a for-loop . It's great to see features used in ways you did not envision when implementing them.
String handling is relatively slow so I understand the impact of removing that. File handling is probably slow because of the component overhead. The actual file is always read in full to memory before the components see the content so there are no slow file streaming involved.
String handling is relatively slow so I understand the impact of removing that. File handling is probably slow because of the component overhead. The actual file is always read in full to memory before the components see the content so there are no slow file streaming involved.
Hi guys,
*CurInStream := TZInputStream.CreateFromFile(S,True)
K
Whoops. I initially thought this was the case, then inspected the source and must have completely missed line 178* as I thought i was mistaken Anyway, it does make you wonder how significant the performance penalty of FileMoveData's ( silly ) char to float conversion is in the entire overhead of the call.Villek wrote:File handling is probably slow because of the component overhead. The actual file is always read in full to memory before the components see the content so there are no slow file streaming involved.
*CurInStream := TZInputStream.CreateFromFile(S,True)
K