-
Notifications
You must be signed in to change notification settings - Fork 303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for other data types in potree pointclouds #767
Conversation
src/Parser/PotreeBinParser.js
Outdated
}, | ||
// Note: at the time of writing, PotreeConverter will only generate normals in Oct16 format | ||
// see PotreeConverter.cpp:121 | ||
// keeping all the values to stay iso to PointAttributes.cpp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually keeping all the values to be able to read clouds converted with old version. I'm editing that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some comments. Do you get better performance than using an interleavedarraybuffer (assuming it is possible) ?
src/Parser/PotreeBinParser.js
Outdated
attr.array[pntIdx * attr.numElements + 2]); | ||
tightbbox.min.min(tmp); | ||
tightbbox.max.max(tmp); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would not it be simpler to use geometry.computeBoundingBox()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. I've checked the implementation, and it is essentially the same as here. It'll add a for loop, but I don't expect its cost to be perceivable.
src/Process/PointCloudProcessing.js
Outdated
@@ -165,6 +165,7 @@ export default { | |||
if (elt.obj) { | |||
elt.obj.material.visible = true; | |||
elt.obj.material.size = layer.pointSize; | |||
elt.obj.material.uniforms.mode.value = layer.mode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that would break using a material with no mode
uniform.
you could instead set here elt.obj.material.mode = layer.mode;
and update the uniform inside the updateUniforms
function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, what about passing layer
to updateUniforms
? That's more or less the pattern we used in iTowns: update uniforms according to some config values attached to layer. It'll make it explicit, and would avoid yet another value copy (they are getting difficult to follow).
two ideas:
|
I just didn't think about using it :-) Thanks for the idea, I'm gonna try.
Interesting paper! For a basic render for testing purpose, I'd prefer to have something simpler (this paper technique necessitates a special pre-processing (so not all converted pointclouds can be displayed with it) and multipass rendering). I was thinking of doing exactly what your first proposal is, but couldn't decide if this was worth it here or not. After all, once you have the classification in the shader, applying some color is very easy and we could let app developers decide how they want to render it, because they'll know their classification, the number of them etc.? As I was not sure, I thought it better to leave it out for now and add it later once our mind is made, WDYT? Btw, I didn't upload a screenshot of normal rendering! This pointcloud is lion_takanawa present in the potree repository. I'll look into the licence to see if we can add it to itowns-sample-data (and get the las, that'll be great to test oct16 normal rendering). |
@mbredif concerning InterleavedBuffer, the way they are implemented in THREE.js seems to lack an important feature: you cannot specify the stride in bytes. You can specify the itemSize and the offset in InterleavedBufferAttribute, and you can specify the stride in terms of number of typed-array elements. This works fine if one point's bytesize is a multiple of your data type for one attribute, but it's not always the case. For example, if my datas are positions (32 bits) and classification (8 bits), I can specify a stride of 5 for the classification buffer, but there's no way I can specify the right stride for the positions buffer. Do you reach the same conclusion as I did? Or did I misunderstand something? If you do I'll ask a question/open an issue in the THREE.js repository. |
updated (@iTowns/reviewers and @mbredif ) |
I also pushed a proposal about the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just left a couple comments, looks good overall.
}, | ||
INTENSITY: { | ||
numElements: 1, | ||
numByte: 2, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you should add a comment to explain why you need this? (AFAIU it's because Float16Array
doesn't exist so you emulate it)
src/Parser/PotreeBinParser.js
Outdated
for (let pntIdx = 0; pntIdx < numPoints; pntIdx++) { | ||
for (const attr of attrs) { | ||
for (let elemIdx = 0; elemIdx < attr.numElements; elemIdx++) { | ||
const fnName = `getUint${(attr.numByte || attr.array.BYTES_PER_ELEMENT) * 8}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be done in the parent loop since it doesn't depend on elemIdx
.
src/Renderer/PointsMaterial.js
Outdated
} | ||
|
||
enablePicking(pickingMode) { | ||
// we don't want pixels to blend over already drawn pixels | ||
this.blending = pickingMode ? NoBlending : NormalBlending; | ||
this.uniforms.pickingMode.value = pickingMode; | ||
if (pickingMode) { | ||
this.oldMode = this.uniforms.mode.value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this.oldMode
be also assigned in the constructor (like @elemoine mentionned here #719 (comment))?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, at least initialized to an empty value, if we want to rely entirely on the update mechanism.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this code looks buggy : if you enable picking twice in a row, oldmode will be PICKING. A more sensible invariant could be "oldmode holds the last non-picking mode used" and could be initialized by the initial mode (or whatever if the initial mode is picking)
@peppsac updated and rebased. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That works great already ! here are some remarks.
Concerning interleaved buffer attributes, there seems to be issues in webgl when attributes are not type-aligned, hence the restriction. That means that creating an interleaved buffer with no attribute decoding may not always be possible. a possibility would be to have a hint option that tentatively tries it and defaults to non interleaved buffers if decoding is required.
src/Parser/PotreeBinParser.js
Outdated
attributeName: attr.attributeName, | ||
normalized: attr.normalized, | ||
array: new attr.arrayType(attr.numElements * numPoints), | ||
numByte: attr.numByte, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could apply the default here and be done with it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what you mean by that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here : numByte: attr.numByte || attr.array.BYTES_PER_ELEMENT,
elsewhere: attr.numByte
instead of attr.numByte || attr.array.BYTES_PER_ELEMENT
src/Parser/PotreeBinParser.js
Outdated
positions[3 * i + 2] = view.getUint32(offset + 8, true); | ||
for (let pntIdx = 0; pntIdx < numPoints; pntIdx++) { | ||
for (const attr of attrs) { | ||
const fnName = `getUint${(attr.numByte || attr.array.BYTES_PER_ELEMENT) * 8}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could move out of the pntIdx
loop, by setting it as a function of attr
in its definition loop.
Maybe a comment on why getUint* works even on all types (float, Int...) (It looks strange to me)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could move out of the pntIdx loop, by setting it as a function of attr in its definition loop.
What do we gain by that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a comment on why getUint* works even on all types (float, Int...) (It looks strange to me)
it doesn't work on all types, it's just that potree converter stores everything as int, then putting an int into a Float32Array will convert it into a float.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do we gain by that?
numPoints
times less evaluations of view[`getUint${(attr.numByte || attr.array.BYTES_PER_ELEMENT) * 8}`]
(and cleaner code IMHO :))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
by setting it as a function of attr in its definition loop.
you mean as a property of attr then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it doesn't work on all types, it's just that potree converter stores everything as int, then putting an int into a Float32Array will convert it into a float.
Indeed, by looking into it, potree is relying on a custom view object rather than a DataView. I am not sure the 2 approaches are strictly equivalent (It is not clear to me who is manipulating bytes and who is doing type casting), but if your approach works, then let us go with it !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean as a property of attr then?
yes
src/Provider/PointCloudProvider.js
Outdated
@@ -138,7 +139,7 @@ function loadPointFile(layer, url) { | |||
if (layer.metadata.customBinFormat) { | |||
return PotreeCinParser.parse(ab).then(result => addPickingAttribute(result)); | |||
} else { | |||
return PotreeBinParser.parse(ab).then(result => addPickingAttribute(result)); | |||
return PotreeBinParser.parse(ab, layer.metadata.pointAttributes).then(result => addPickingAttribute(result)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe the whole layer.metadata
should be given for future parsers to consume it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure yet, that's why I did an adhoc call here. I think this question should be tackled when/if we have a parser registry mechanism for pointclouds, WDYT? Not yet sure how this will be done though (there are probably things that can be unified with 3dTiles pnts format I guess).
src/Renderer/PointsMaterial.js
Outdated
} | ||
|
||
enablePicking(pickingMode) { | ||
// we don't want pixels to blend over already drawn pixels | ||
this.blending = pickingMode ? NoBlending : NormalBlending; | ||
this.uniforms.pickingMode.value = pickingMode; | ||
if (pickingMode) { | ||
this.oldMode = this.uniforms.mode.value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this code looks buggy : if you enable picking twice in a row, oldmode will be PICKING. A more sensible invariant could be "oldmode holds the last non-picking mode used" and could be initialized by the initial mode (or whatever if the initial mode is picking)
src/Renderer/Shader/PointsVS.glsl
Outdated
#define MODE_CLASSIFICATION 3 | ||
#define MODE_NORMAL 4 | ||
#define MODE_NORMAL_OCT16 5 | ||
#define MODE_NORMAL_SPHEREMAPPED 6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to prevent the replication of code values, material.defines could be used instead
src/Renderer/Shader/PointsVS.glsl
Outdated
} else if (mode == MODE_NORMAL_OCT16) { | ||
vColor = vec4(decodeOct16Normal(oct16Normal), 1.0); | ||
} else if (mode == MODE_NORMAL_SPHEREMAPPED) { | ||
vColor = vec4(decodeSphereMappedNormal(sphereMappedNormal), 1.0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
normals range from -1 to 1. maybe you can take the absolute value of it.
5d387f0
to
23da8d3
Compare
@iTowns/reviewers updated. Also pinging @mbredif: I think I have adressed all your remarks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apart from the inline comments, I have 2 concerns :
-
how well does it work with the stock
THREE.PointsMaterial
? you can browse here :examples/pointcloud.html?material=three
. Ensuring it works well would prevent too much coupling withitowns.PointsMaterial
... -
I find the
oldMode
logic a bit convoluted.
Maybe you could havethis.picking=false
andthis.mode=MODE.COLOR
in the constructor, remove theoldMode
property and theenablePicking
function (just setmaterial.picking
instead), and do this inupdateUniforms
:
this.blending = pickingMode ? NoBlending : NormalBlending;
this.uniforms.mode.value = this.picking ? MODE.PICKING : this.mode;
src/Renderer/PointsMaterial.js
Outdated
|
||
this.uniforms.size = new Uniform(this.size); | ||
this.uniforms.mode = new Uniform(this.mode); | ||
this.uniforms.pickingMode = new Uniform(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused, to be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, thanks for the catch :-)
src/Provider/PointCloudProvider.js
Outdated
layer.metadata.pointAttributes.find(elem => elem.startsWith('NORMAL')); | ||
if (normal) { | ||
layer.material.defines[normal] = 1; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about an opt-in isNormal
property for pointAttributes ? that would prevent having a dependency on this specific material...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The more general question is: do we expect users to be able subclass Material
and not ShaderMaterial
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that would prevent having a dependency on this specific material
IMHO, we don't need to impose ourselves too much constraints. In the same way that TileMesh/LayeredMaterial is not replaceable by another mesh/material, we should totally rely on PointsMaterial to implement our features.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree, the less coupling, the more itowns is untangled, usable and extensible... I would rather set opt-in hint properties to materials so that materials could honor the hint if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On this instance, we don't really care. The only "coupling" was the fact layer.material.defines was undefined (!). The current code works with a basic three material.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely agree here. (We'll talk again about it when we will want to have the same functionality with the same material applied to pointclouds parsed from PNTS, PLY, LAS, LAZ...)
src/Parser/PotreeBinParser.js
Outdated
// Potree stores everything as int, and uses scale + offset | ||
fnName: `getUint${numByte * 8}`, | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apart from array
, this could be done statically once and for all in the global scope
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I don't see what you mean, because I feel I can't. I'm constructing an array of attr that depends on pointAttributes. The point of this code is to copy the POINT_ATTTRIBUTES
static array.
src/Parser/PotreeBinParser.js
Outdated
positions[3 * i + 2] = view.getUint32(offset + 8, true); | ||
for (let pntIdx = 0; pntIdx < numPoints; pntIdx++) { | ||
for (const attr of attrs) { | ||
for (let elemIdx = 0; elemIdx < attr.numElements; elemIdx++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the previous attr loop goes in global scope, the array could be instanced here (just after for (const attr of attrs)
) and added to the geometry at the end of the iteration instead of in another loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put some code please, that will be faster and clearer.
To understand a bit more what you're after, can you tell me a bit more about your use case for other Materials? Otherwise, please note that the debug tools won't completely work with
It's a bit convoluted, but for the sake of being clearer for the user: picking is mutually exclusive with other display modes. Having a separate boolean to set it will complicate things from a user POV. |
Ideally, I would rather not have any coupling between individual parsers, providers and materials, meaning (the includes in the potree provider would just be there to provide default parsers and materials, that could be overriden) :
... unless we specify the opt-in
I agree from a shader perspective that these are exclusive modes, but from a user perspective (and that is how we use it!), picking is more like an overriding mode which deserves (IMHO) an overriding property. For symmetry, I suggest to go from the boolean |
Note that, even I had the rights, I would not block this PR, I am just expressing my opinion on the coupling it reintroduces... However that can be addressed in a follow up PR (eg: for the equivalent PR on PNTS pointclouds) |
ad0c4e3
to
58c6c21
Compare
005bb62
to
371ae5a
Compare
FWIW, I agree with both of you:
|
Now that I have reviewed most of the itowns codebase, I suggest to :
|
So I kept your first commit @mbredif (thanks for the simplification). As for the second, I suggest to open a PR with it (I'd be glad to r+ it). I have pushed it in its own branch (https://github.com/iTowns/itowns/tree/points_shader_refacto). I reintroduced a picking boolean as you and @peppsac suggested, and restored the compatibility with THREE material (I even adapted PointCloudDebug to support it by making picking and mode opt-in). @iTowns/reviewers please r? :-) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good (I left a suggestion for the shader code)
src/Provider/PointCloudProvider.js
Outdated
layer.metadata.pointAttributes.find(elem => elem.startsWith('NORMAL')); | ||
if (normal) { | ||
layer.material.defines[normal] = 1; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that would prevent having a dependency on this specific material
IMHO, we don't need to impose ourselves too much constraints. In the same way that TileMesh/LayeredMaterial is not replaceable by another mesh/material, we should totally rely on PointsMaterial to implement our features.
src/Renderer/Shader/PointsVS.glsl
Outdated
} else if (mode == MODE_INTENSITY) { | ||
vColor = vec4(intensity, intensity, intensity, opacity); | ||
} else if (mode == MODE_NORMAL) { | ||
#if defined(NORMAL_OCT16) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to separate the normal
parsing from colorisation mode.
Something like:
#if defined(NORMAL_OCT16)
vec3 normal = decodeOct16Normal(...);
#elif defined(NORMAL_SPHEREMAPPED)
vec3 normal = decodeSphereMappedNormal(sphereMappedNormal);
#elif defined(NORMAL)
// nothing to do
#else
vec3 normal;
#endif
if (pickingMode) {
vColor = unique_id;
vColor = unique_id;
} else if (mode == MODE_INTENSITY) {
vColor = vec4(intensity, intensity, intensity, opacity);
} else if (mode == MODE_NORMAL) {
vColor = vec4(abs(normal), opacity);
} else {
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, I wrote a commit about it with shader chunks : 58c6c21
const assert = require('assert'); | ||
|
||
|
||
describe('PotreeBinParser', function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should add tests on :
PointCloudProvider.js
PointsMaterial.js
😫 @gchoqueux you should take a step back and think if you're attitude is really helping the itowns project (hint: it is not). |
@gchoqueux what test I can add for these 2 modules is unclear to me. Can you push a test example in a branch to see what you have in mind? |
@gchoqueux I pushed a test for |
@iTowns/reviewers please r? again :-) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems good to me, I don't see either which test could be added to PointsMaterial
.
@@ -272,3 +274,7 @@ export default { | |||
}); | |||
}, | |||
}; | |||
|
|||
export const _testing = { | |||
parseMetadata, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not okay with this, please move it to the default export, even if I understand that this is for testing purpose only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your review. Can you elaborate on the reasons you don't like this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To elaborate on my position, I'm pretty against exposing this to the default export, because it's a private method that has no reason to be exposed. Once you expose such a method, it becomes part of the public api, which means we have an implicit commitment to it. I know that this way of thinking is not very common on this project, but I'd like not to introduce api breakage when we can avoid it. For me _testing
is a good middle-ground as it conveys the fact it is a private method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really know, maybe in fact it is a good idea. We could agree on a special exported namespace we could use to test internal methods. So in fact maybe keep it and find a more suitable name ? _testing
feels muddled to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once you expose such a method, it becomes part of the public api
Providers are not in the public API so no problems here, but see my change of pov above ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Providers are not in the public API so no problems here
Just a quick note on this: I'm afraid they are. Everything importable from a ES6 configuration is part of the public API and we should treat it that way. Every transpiled file in lib
is importable. Actually, in my opinion this should be the preferred way of using iTowns. The 2 Main
files are just convenience.
So in fact maybe keep it and find a more suitable name ? _testing feels muddled to me.
I don't know, testing
seemed a suitable name for an export for testing purpose... I don't see a better name (yet).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I see one possible solution: surround testing exports with __DEBUG__
tests. But I need to add support for this in our .babelrc
. So as not to enter into a rabbit hole for this PR, I suggest to keep it that way here and revisit it later. Is it acceptable for you @zarov?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(and then rename __DEBUG__
with __DEV__
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(sorry for being so undecided) yeah testing
is good :D But if you feel that surrounding testing with those is better, let's go for it.
Btw, I'm not quite happy with the name and the return value of |
as parseMetadata and parseOctree are parse commands, should not they be part of a 'parsers/PotreeCloudParser.js' file (or something similar) ? |
Not sure. We use |
actually, |
Description
We were only supporting position and rgb informations from potree converted pointclouds. This PR aims at supporting all of them. At least I wanted to have them parsed in attributes, but I also added some default rendering to check that it is working correctly.
Some remarks: