Three.js: PLYloader error in loading little_endian geometry

Created on 19 Sep 2018  路  12Comments  路  Source: mrdoob/three.js

Greetings

Description of the problem

I am using your library to render some PLY files generated from third party cameras. As such, I have little control upon the generated file.
The generated PLY is encoded in binary_little_endian format, and is a correctly generated PLY file (I can load it via MeshLab, or I can translate to a similar format such as Nexus without problems).
Yet, I cannot correctly load this file via the PLYloader.
More precisely: I am able to render the 'ascii'-ized version of the file (loaded into MeshLab, and saved in ascii format) with little problem, yet the binary file simply won't load.

This seems due to an error in the windowing of the underlying array buffer, for I get this error:

RangeError: offset is outside the bounds of the DataView

I tried to alter the loader so that the offset cannot exceed the DataView boundary, but as expected this causes other errors downstream

THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.

I'd like to ask for some informations on this case: is it an error of the loader, of the file, of the parameters I set in the program (an example of which is created in a JSFiddle)?

The fiddle has a reference to the PLY files, and I'll be happy to add any missing information.

Thanks for your product, and for the time spent in reading this.

Three.js version
  • [x] Dev
  • [x] r96
  • [x] Possibly older ones, but I cannot find the version number
Browser
  • [x] All of them
  • [ ] Chrome
  • [ ] Firefox
  • [ ] Internet Explorer
OS
  • [x] All of them
  • [ ] Windows
  • [ ] macOS
  • [ ] Linux
  • [ ] Android
  • [ ] iOS
Hardware Requirements (graphics card, VR Device, ...)

None

Bug

Most helpful comment

The binary file in the fiddle appears to be slightly truncated at the very end. The header lists 432486 faces, but the file ends after face 432443. PLYloader is throwing a RangeError trying to read the last few faces, since there's no more data to be had, and the postProcess function where it writes the data into the geometry never gets called, which is why the geometry looks empty.

It appears that MeshLab is sensibly taking as many faces as it can get and not worrying about the file not having quite as many as the header promised.

Fundamentally, the bug is with whatever is writing this file. I'd suspect that what's happening is that a few of the faces are invalid (by whatever definition the program that's doing the writing uses; maybe it's skipping faces that reference missing vertices, maybe it's skipping faces that have one vertex referenced twice; who knows...) and as it writes it's skipping the problematic faces. Thus at the beginning when it writes the header it thinks it's going to have 432486 faces, but a few don't get written along the way and it doesn't go back and update the header to reflect the 432443 faces it actually wrote. It's not an uncommon failure mode for PLY writers. Not a huge problem if it happens on the last element in the file, but if it happens on one of the earlier elements, then it corrupts the data in the following element (e.g. if you have a couple of missing vertices, then the first few faces will be incorrectly interpreted as vertices -- with an ascii ply you can usually detect that, but with a binary ply you just get a couple of garbage verts).

If desired behavior is silently supporting slightly malformed files like this, the way MeshLab does, it should be easy enough to wrap the call to binaryReadElement in parseBinary to catch the RangeError and ignore it.

As a side note, if you're looking to validate a ply implementation, RPLY is probably the canonical C implementation and the code is fairly straightforward.

All 12 comments

PLYLoader should actually support binary files encoded in little endian format. There is a LE binary file in the repo and it loads fine 馃 https://jsfiddle.net/n8xzLc1m/23/

I'd like to ask for some informations on this case: is it an error of the loader, of the file, of the parameters I set in the program (an example of which is created in a JSFiddle)?

Your fiddle looks fine to me. I'm not sure if the loader has a problem or the file. In any event, I can import your PLY file in Blender without problems.

I'm out of ideas 馃槥. I don't know why but the mentioned PLY file is processed fundamentally wrong by PLYLoader. For some reasons, the loader reads only zeros when processing the indices. Besides, I think the data in the position attribute are incorrect, too. Single vertices have values like this (-37036072,1.4753827330473196e-35,-1.6501715689228802). These are definitely not the data I see in Blender when importing the mentioned PLY file. The rendering result should actually look like so:

image

Yes, the rendered image is actually flipped upside down relative to what was actually scanned (probably a bug of the tool that created the PLY, but should be inconsequent as long as I can rotate the image).

I thank you for the time you spent in trying to analyze the problem... I hope that someone can come up with some flash of idea that actually finds the problem (in the loader or in the PLY data), so that it can be patched.

I've tried another JavaScript PLY parser but the project was also not able to parse the file. Too bad, I hoped we could check out what the parser makes differently compared to PLYLoader.

https://github.com/mikolalysenko/parse-ply

How does it look like in meshlab?

It looks good 馃槉

image

Okay, I'll check out their code and try to find something interesting.

If it may help: I tried to convert it by using the [Nexus library] (https://github.com/cnr-isti-vclab/nexus)... And the resulting file is loaded correctly by the loader for Three.js they developed together with the library.
I must admit that I don't actually understand C/C++ or the PLY format specifications well enough to understand what the compiling steps actually did to the code, but it sonhow worked.
I also tried with some other working (as in 'working with this loader') PLY files, and they too were compiled correctly.
...Well, yeah, probably this post is not actually helpful at all, but I thought that it may be a different take on the problem: the file looks corrupted by this loader, but may it be that other tools (the aforementioned Nexus, but also Meshlab) are actually some more 'fault tolerant' in respect to this format?

@algrs Do you might have any ideas why PLYLoader can't process the mentioned binary file?

MeshLab internally used VCGLib in order to import PLY files. I've checked out the respective C++ source code but unfortunately I could not figure it out so far what they make different in order to load the problematic binary file.

The binary file in the fiddle appears to be slightly truncated at the very end. The header lists 432486 faces, but the file ends after face 432443. PLYloader is throwing a RangeError trying to read the last few faces, since there's no more data to be had, and the postProcess function where it writes the data into the geometry never gets called, which is why the geometry looks empty.

It appears that MeshLab is sensibly taking as many faces as it can get and not worrying about the file not having quite as many as the header promised.

Fundamentally, the bug is with whatever is writing this file. I'd suspect that what's happening is that a few of the faces are invalid (by whatever definition the program that's doing the writing uses; maybe it's skipping faces that reference missing vertices, maybe it's skipping faces that have one vertex referenced twice; who knows...) and as it writes it's skipping the problematic faces. Thus at the beginning when it writes the header it thinks it's going to have 432486 faces, but a few don't get written along the way and it doesn't go back and update the header to reflect the 432443 faces it actually wrote. It's not an uncommon failure mode for PLY writers. Not a huge problem if it happens on the last element in the file, but if it happens on one of the earlier elements, then it corrupts the data in the following element (e.g. if you have a couple of missing vertices, then the first few faces will be incorrectly interpreted as vertices -- with an ascii ply you can usually detect that, but with a binary ply you just get a couple of garbage verts).

If desired behavior is silently supporting slightly malformed files like this, the way MeshLab does, it should be easy enough to wrap the call to binaryReadElement in parseBinary to catch the RangeError and ignore it.

As a side note, if you're looking to validate a ply implementation, RPLY is probably the canonical C implementation and the code is fairly straightforward.

but if it happens on one of the earlier elements, then it corrupts the data in the following element

How is it possible to detect this error situation? Even if you avoid the mentioned RangeError of the initial post, the buffer data processed so far seemed to be wrong. Like mentioned before, there are parsed vertices with values like (-37036072,1.4753827330473196e-35,-1.6501715689228802) which are obviously malformed. If you import the PLY file in Blender, such a point lies outside of the respective bounding box.

Interesting -- so it turns out that the issue was actually an off-by-one error having to do with whitespace in the ascii header... Fix is https://github.com/mrdoob/three.js/pull/15099 I'm still puzzling over why that problem was causing the data to appear truncated, since if anything there should have been one extra byte left over after everything was read.

Thank very much to all of You!!

Was this page helpful?
0 / 5 - 0 ratings