Skip to content

Commit feecb04

Browse files
author
Leon
committed
PCDLoader: read label by type/size; examples: Z-up fit view and label-based colors
- Read label attribute using header-derived type and size in binary and binary_compressed Examples (webgl_loader_pcd.html): - Add Z-up handling and fit-view camera for Z-up datasets - Recreate OrbitControls per up-axis to avoid roll on pitch - Colorize points by integer label (deterministic HSL mapping) - Hide GUI color control when label-based vertex colors are present - Keep legacy behavior for Y-up files; split file lists (yUpFiles/zUpFiles) - Update default example to 'binary/fusion_lidar_scene.pcd'
1 parent 798408f commit feecb04

File tree

4 files changed

+133
-18
lines changed

4 files changed

+133
-18
lines changed

examples/jsm/loaders/PCDLoader.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,11 @@ class PCDLoader extends Loader {
515515
}
516516

517517
if ( offset.label !== undefined ) {
518-
519-
const labelIndex = PCDheader.fields.indexOf( 'label' );
520-
label.push( dataview.getInt32( ( PCDheader.points * offset.label ) + PCDheader.size[ labelIndex ] * i, this.littleEndian ) );
518+
519+
const labelIndex = PCDheader.fields.indexOf('label')
520+
const labelType = PCDheader.type[labelIndex]
521+
const labelSize = PCDheader.size[labelIndex]
522+
label.push(this._getDataView(dataview, (PCDheader.points * offset.label) + labelSize * i, labelType, labelSize))
521523

522524
}
523525

@@ -577,7 +579,10 @@ class PCDLoader extends Loader {
577579

578580
if ( offset.label !== undefined ) {
579581

580-
label.push( dataview.getInt32( row + offset.label, this.littleEndian ) );
582+
const labelIndex = PCDheader.fields.indexOf('label')
583+
const labelType = PCDheader.type[labelIndex]
584+
const labelSize = PCDheader.size[labelIndex]
585+
label.push(this._getDataView(dataview, row + offset.label, labelType, labelSize))
581586

582587
}
583588

40.4 MB
Binary file not shown.
2.17 MB
Binary file not shown.

examples/webgl_loader_pcd.html

Lines changed: 124 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,19 @@
2929
import { PCDLoader } from 'three/addons/loaders/PCDLoader.js';
3030
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
3131

32-
let camera, scene, renderer;
32+
const yUpFiles = [
33+
'ascii/simple.pcd',
34+
'binary/Zaghetto.pcd',
35+
'binary/Zaghetto_8bit.pcd',
36+
'binary_compressed/pcl_logo.pcd'
37+
];
38+
39+
const zUpFiles = [
40+
'binary_compressed/lidar_scene.pcd',
41+
'binary/fusion_lidar_scene.pcd'
42+
];
43+
44+
let camera, scene, renderer, controls;
3345

3446
init();
3547
render();
@@ -47,11 +59,6 @@
4759
camera.position.set( 0, 0, 1 );
4860
scene.add( camera );
4961

50-
const controls = new OrbitControls( camera, renderer.domElement );
51-
controls.addEventListener( 'change', render ); // use if there is no animation loop
52-
controls.minDistance = 0.5;
53-
controls.maxDistance = 10;
54-
5562
//scene.add( new THREE.AxesHelper( 1 ) );
5663

5764
const loader = new PCDLoader();
@@ -61,21 +68,122 @@
6168
loader.load( './models/pcd/' + file, function ( points ) {
6269

6370
points.geometry.center();
64-
points.geometry.rotateX( Math.PI );
6571
points.name = file;
6672
scene.add( points );
6773

74+
// If geometry has integer label attribute, colorize by label
75+
const labelAttr = points.geometry.getAttribute( 'label' );
76+
if ( labelAttr ) {
77+
78+
const count = labelAttr.count;
79+
const colors = new Float32Array( count * 3 );
80+
const labelToColor = new Map();
81+
const tmp = new THREE.Color();
82+
83+
for ( let i = 0; i < count; i ++ ) {
84+
85+
const label = labelAttr.getX( i );
86+
let c = labelToColor.get( label );
87+
if ( c === undefined ) {
88+
// Deterministic label -> color mapping via HSL
89+
const hue = ( ( ( label * 37 ) % 360 ) + 360 ) % 360 / 360; // [0,1)
90+
tmp.setHSL( hue, 0.6, 0.5 );
91+
c = tmp.clone();
92+
labelToColor.set( label, c );
93+
}
94+
95+
colors[ i * 3 + 0 ] = c.r;
96+
colors[ i * 3 + 1 ] = c.g;
97+
colors[ i * 3 + 2 ] = c.b;
98+
99+
}
100+
101+
points.geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
102+
if ( points.material && 'vertexColors' in points.material ) points.material.vertexColors = true;
103+
104+
}
105+
106+
// For car_scene (Z-up data), use Z-up camera and fit view
107+
if ( zUpFiles.includes(file) ) {
108+
109+
// Use Z-up for camera and controls
110+
camera.up.set( 0, 0, 1 );
111+
112+
// Recreate OrbitControls to avoid roll from prior Y-up init
113+
if ( controls ) controls.dispose();
114+
controls = new OrbitControls( camera, renderer.domElement );
115+
controls.addEventListener( 'change', render );
116+
controls.minDistance = 0.5;
117+
controls.maxDistance = 1000;
118+
controls.screenSpacePanning = false; // Use world-up panning
119+
120+
// Compute bounding sphere
121+
if ( ! points.geometry.boundingSphere ) points.geometry.computeBoundingSphere();
122+
const sphere = points.geometry.boundingSphere;
123+
const center = sphere.center.clone();
124+
const radius = sphere.radius || 1;
125+
126+
// Compute distance by FOV to fully frame (fit view)
127+
const fov = camera.fov * Math.PI / 180;
128+
const distance = radius / Math.sin( fov / 2 );
129+
130+
// Set near/far to cover the whole cloud
131+
camera.near = Math.max( 0.001, distance * 0.01 );
132+
camera.far = distance * 10 + radius;
133+
camera.updateProjectionMatrix();
134+
135+
// Set controls target to cloud center
136+
controls.target.copy( center );
137+
138+
// Place camera in an oblique top view for Z-up
139+
const viewDir = new THREE.Vector3( 0, -1, 0.5 ).normalize();
140+
camera.position.copy( center ).add( viewDir.multiplyScalar( distance ) );
141+
camera.lookAt( center );
142+
// Clamp polar angle to avoid flip; update matrices
143+
controls.minPolarAngle = 0.01;
144+
controls.maxPolarAngle = Math.PI - 0.01;
145+
controls.update();
146+
camera.updateProjectionMatrix();
147+
}
148+
else {
149+
150+
// Non car_scene: keep legacy rotateX(Math.PI) for display
151+
points.geometry.rotateX( Math.PI );
152+
153+
// Restore default camera (Y-up) and basic params
154+
camera.up.set( 0, 1, 0 );
155+
camera.near = 0.01;
156+
camera.far = 40;
157+
camera.updateProjectionMatrix();
158+
159+
// Recreate OrbitControls for default Y-up semantics
160+
if ( controls ) controls.dispose();
161+
controls = new OrbitControls( camera, renderer.domElement );
162+
controls.addEventListener( 'change', render );
163+
controls.minDistance = 0.5;
164+
controls.maxDistance = 1000;
165+
controls.screenSpacePanning = true;
166+
167+
// Restore initial position and target
168+
controls.target.set( 0, 0, 0 );
169+
camera.position.set( 0, 0, 2 );
170+
camera.lookAt( 0, 0, 0 );
171+
controls.update();
172+
camera.updateProjectionMatrix();
173+
174+
}
175+
68176
const gui = new GUI();
69177

70178
gui.add( points.material, 'size', 0.001, 0.01 ).onChange( render );
71-
gui.addColor( points.material, 'color' ).onChange( render );
179+
// Hide color control when geometry has label-based vertex colors
180+
if ( ! labelAttr ) {
181+
gui.addColor( points.material, 'color' ).onChange( render );
182+
}
72183
gui.add( points, 'name', [
73-
'ascii/simple.pcd',
74-
'binary/Zaghetto.pcd',
75-
'binary/Zaghetto_8bit.pcd',
76-
'binary_compressed/pcl_logo.pcd',
184+
...yUpFiles,
185+
...zUpFiles,
77186
] ).name( 'type' ).onChange( e => {
78-
79187
gui.destroy();
80188
scene.remove( points );
81189
loadPointCloud( e );
@@ -89,7 +197,9 @@
89197

90198
};
91199

92-
loadPointCloud( 'binary/Zaghetto.pcd' );
200+
// loadPointCloud( 'binary/Zaghetto.pcd' );
201+
202+
loadPointCloud( 'binary/fusion_lidar_scene.pcd' );
93203

94204
window.addEventListener( 'resize', onWindowResize );
95205

0 commit comments

Comments
 (0)