pywavefront / PyWavefront

Python library for importing Wavefront .obj files
BSD 3-Clause "New" or "Revised" License
312 stars 80 forks source link

BIN and JSON cached files #115

Open morawi opened 4 years ago

morawi commented 4 years ago

I would like to read the generated BIN and JSON (when cache=True) directly via simple function into a Wavefront object. That is, I would like to read these two files myself and feed them to Wavefront constructor. I wonder if this can be done via some auxiliary function?

einarf commented 4 years ago

It does already load from cached files if they exist and the cache parameter is True

morawi commented 4 years ago

@einarf that's totally correct, but I would like to use those streams in my application (stream of bytes and not as a file); in fact, I would like to store them in a single file with some other data; then when I read the file I would like to construct the OBJ from those streams. One way to do this is to load those BIN and JSON file and store them into my file, then, at the time of loading; save them as BIN and JSON files and construct the object with cache=True, but that would be cumbersome. Anyways, could you please guide me where in the code the loading happens? Maybe I can tweak it to my case.

einarf commented 4 years ago

I don't think you can fully reconstruct the obj from the bin/json file. They are mainly made so users can easily dump them into graphics memory.

I do have a library overriding the cache loader that just uploads the byte streams directly to the gpu instead of reconstructing the obj itself (much faster) https://github.com/moderngl/moderngl-window/blob/4c3cd385718517a8055e8c271195f9100eaf2a66/moderngl_window/loaders/scene/wavefront.py#L52-L69

Cache loading triggers from this : https://github.com/pywavefront/PyWavefront/blob/caa2013d7026dd484ac8c5ca8273f2d680196130/pywavefront/obj.py#L86-L90

Loader and saver is here : https://github.com/pywavefront/PyWavefront/blob/master/pywavefront/cache.py

morawi commented 4 years ago

What I wanted is to pass data instead of reading it via data = json.loads(fd.read()) (line 193) and other info red via file handler fd = gzip.open(str(cache_name(self.file_name)), 'rb') (lines 109-119) to the class constructor, but I guess that would disrupt the integrity of PyWavefront.

Hence, the best way to do this by allowing my application to save the cached files (BIN and JSON), same for the .mtl file if used, before calling PyWavefront (I am thus removing the original .obj file, and). I did a few tests and it worked even after xx.obj file has been removed, as long as cache=True and xx.obj.bin and xx.obj.json are available.

einarf commented 4 years ago

Yup. that seems reasonable. You can even replace the cache loader / saver classes here if you need more flexibility : https://github.com/pywavefront/PyWavefront/blob/caa2013d7026dd484ac8c5ca8273f2d680196130/pywavefront/obj.py#L50-L51

To make things future proof I would check the version in the json file. I had plans to extend this feature by adding pre-computed bounding boxes for example, but that might not break compatibility.

morawi commented 4 years ago

Thanks you for the heads up! I don't think I need to do this now, but maybe later. The only concern, although trivial, is that when the original file OBJ is cached and then removed, one still needs to use its name when constructing the wavefront .. i.e. via pywavefront.Wavefront('earth.obj', ...) Not sure if it would be a good practice to remove the .obj extension from the name, and then to be added later inside Wavefront class if it is not there. In this case, as an example with earth file, one can either use, pywavefront.Wavefront('earth', ...) or pywavefront.Wavefront('earth.obj', ...)

to construct the object.