|
29 | 29 | import { PCDLoader } from 'three/addons/loaders/PCDLoader.js'; |
30 | 30 | import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; |
31 | 31 |
|
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; |
33 | 45 |
|
34 | 46 | init(); |
35 | 47 | render(); |
|
47 | 59 | camera.position.set( 0, 0, 1 ); |
48 | 60 | scene.add( camera ); |
49 | 61 |
|
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 | | - |
55 | 62 | //scene.add( new THREE.AxesHelper( 1 ) ); |
56 | 63 |
|
57 | 64 | const loader = new PCDLoader(); |
|
61 | 68 | loader.load( './models/pcd/' + file, function ( points ) { |
62 | 69 |
|
63 | 70 | points.geometry.center(); |
64 | | - points.geometry.rotateX( Math.PI ); |
65 | 71 | points.name = file; |
66 | 72 | scene.add( points ); |
67 | 73 |
|
| 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 | + |
68 | 176 | const gui = new GUI(); |
69 | 177 |
|
70 | 178 | 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 | + } |
72 | 183 | 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, |
77 | 186 | ] ).name( 'type' ).onChange( e => { |
78 | | - |
79 | 187 | gui.destroy(); |
80 | 188 | scene.remove( points ); |
81 | 189 | loadPointCloud( e ); |
|
89 | 197 |
|
90 | 198 | }; |
91 | 199 |
|
92 | | - loadPointCloud( 'binary/Zaghetto.pcd' ); |
| 200 | + // loadPointCloud( 'binary/Zaghetto.pcd' ); |
| 201 | + |
| 202 | + loadPointCloud( 'binary/fusion_lidar_scene.pcd' ); |
93 | 203 |
|
94 | 204 | window.addEventListener( 'resize', onWindowResize ); |
95 | 205 |
|
|
0 commit comments