diff --git a/index.html b/index.html index 207d2b5..81874de 100644 --- a/index.html +++ b/index.html @@ -54,6 +54,66 @@ +
diff --git a/main.js b/main.js index 5bea877..6dea69c 100644 --- a/main.js +++ b/main.js @@ -96,184 +96,212 @@ controls.panSpeed = 1.0; controls.target.set(0, 0, 0); // Load the PLY file const loader = new PLYLoader(); -loader.load("./data/01_column.ply", function (plyGeometry) { - const geometry = new THREE.BufferGeometry(); - geometry.setAttribute( - "position", - new THREE.Float32BufferAttribute( - plyGeometry.attributes.position.array, - 3 - ) - ); - geometry.setAttribute( - "color", - new THREE.Float32BufferAttribute(plyGeometry.attributes.color.array, 3) - ); - geometry.needsUpdate = true; - - const planes = [ - new THREE.Vector4(1, 0, 0, 0), - new THREE.Vector4(0, 1, 0, 0), - new THREE.Vector4(0, 0, 1, 0), - ]; - let dir = new THREE.Vector3(1, 1, 1); - const material = new THREE.ShaderMaterial({ - transparent: true, - vertexColors: true, - clipping: true, - uniforms: { - L: { value: 0 }, - R: { value: 100 }, - xPlane: { value: planes[0] }, - yPlane: { value: planes[1] }, - zPlane: { value: planes[2] }, - dir: { value: dir }, - }, - vertexShader: ` - vec3 interpolateColor(float ratio, vec3 color1, vec3 color2) { - return mix(color1, color2, ratio); - } - - vec3 intensityToRGB(float intensity) { - if (intensity <= 0.333) { - return interpolateColor(intensity / 0.333, vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0)); // Blue -> Green - } else if (intensity <= 0.666) { - return interpolateColor((intensity - 0.333) / 0.333, vec3(0.0, 1.0, 0.0), vec3(1.0, 1.0, 0.0)); // Green -> Yellow - } else { - return interpolateColor((intensity - 0.666) / 0.334, vec3(1.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0)); // Yellow -> Red - } - } - - uniform float L; - uniform float R; - varying vec3 vColor; - varying vec3 vPosition; - - void main() { - vPosition = position; - - float intensity = color[0]; - vColor = intensityToRGB(intensity); +const gui = new GUI(); +const selectedName = { name: "Select File" }; +gui.add(selectedName, "name", ["01_column", "02_ground"]) + .name("GPR Example") + .onChange((value) => load(value)); + +let folderArray = []; + +function load(name) { + loader.load(`./data/${name}.ply`, function (plyGeometry) { + scene.clear(); + folderArray.forEach((folder) => { + // gui.removeFolder(folder); + folder.destroy(); + }); - if (intensity < L - 1e-6 || intensity > R + 1e-6) { - gl_Position = vec4(0.0, 0.0, 1e10, 1.0); - } else { - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute( + "position", + new THREE.Float32BufferAttribute( + plyGeometry.attributes.position.array, + 3 + ) + ); + geometry.setAttribute( + "color", + new THREE.Float32BufferAttribute( + plyGeometry.attributes.color.array, + 3 + ) + ); + geometry.needsUpdate = true; + + const planes = [ + new THREE.Vector4(1, 0, 0, 0), + new THREE.Vector4(0, 1, 0, 0), + new THREE.Vector4(0, 0, 1, 0), + ]; + let dir = new THREE.Vector3(1, 1, 1); + const material = new THREE.ShaderMaterial({ + transparent: true, + vertexColors: true, + clipping: true, + uniforms: { + L: { value: 0 }, + R: { value: 100 }, + xPlane: { value: planes[0] }, + yPlane: { value: planes[1] }, + zPlane: { value: planes[2] }, + dir: { value: dir }, + }, + vertexShader: ` + vec3 interpolateColor(float ratio, vec3 color1, vec3 color2) { + return mix(color1, color2, ratio); } - - gl_PointSize = 10.0; - } - `, - fragmentShader: ` - varying vec3 vColor; - varying vec3 vPosition; - uniform vec4 xPlane; - uniform vec4 yPlane; - uniform vec4 zPlane; - uniform vec3 dir; - - void main() { - if ((dir.x == 1.0 && dot(vPosition, xPlane.xyz) > xPlane.w) || - (dir.x == -1.0 && dot(vPosition, xPlane.xyz) < xPlane.w)) { - discard; + + vec3 intensityToRGB(float intensity) { + if (intensity <= 0.333) { + return interpolateColor(intensity / 0.333, vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0)); // Blue -> Green + } else if (intensity <= 0.666) { + return interpolateColor((intensity - 0.333) / 0.333, vec3(0.0, 1.0, 0.0), vec3(1.0, 1.0, 0.0)); // Green -> Yellow + } else { + return interpolateColor((intensity - 0.666) / 0.334, vec3(1.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0)); // Yellow -> Red + } } - if ((dir.y == 1.0 && dot(vPosition, yPlane.xyz) > yPlane.w) || - (dir.y == -1.0 && dot(vPosition, yPlane.xyz) < yPlane.w)) { - discard; + + uniform float L; + uniform float R; + varying vec3 vColor; + varying vec3 vPosition; + + void main() { + vPosition = position; + + float intensity = color[0]; + vColor = intensityToRGB(intensity); + + if (intensity < L - 1e-6 || intensity > R + 1e-6) { + gl_Position = vec4(0.0, 0.0, 1e10, 1.0); + } else { + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + + gl_PointSize = 4.0; } - if ((dir.z == 1.0 && dot(vPosition, zPlane.xyz) > zPlane.w) || - (dir.z == -1.0 && dot(vPosition, zPlane.xyz) < zPlane.w)) { - discard; + `, + fragmentShader: ` + varying vec3 vColor; + varying vec3 vPosition; + uniform vec4 xPlane; + uniform vec4 yPlane; + uniform vec4 zPlane; + uniform vec3 dir; + + void main() { + if ((dir.x == 1.0 && dot(vPosition, xPlane.xyz) > xPlane.w) || + (dir.x == -1.0 && dot(vPosition, xPlane.xyz) < xPlane.w)) { + discard; + } + if ((dir.y == 1.0 && dot(vPosition, yPlane.xyz) > yPlane.w) || + (dir.y == -1.0 && dot(vPosition, yPlane.xyz) < yPlane.w)) { + discard; + } + if ((dir.z == 1.0 && dot(vPosition, zPlane.xyz) > zPlane.w) || + (dir.z == -1.0 && dot(vPosition, zPlane.xyz) < zPlane.w)) { + discard; + } + + gl_FragColor = vec4(vColor, 1.0); } + `, + }); - gl_FragColor = vec4(vColor, 1.0); - } - `, - }); - - document.addEventListener("mousemove", (e) => { - handleMouseMove(e); - const lowerValue = parseInt(lowerHandle.style.left); - const upperValue = parseInt(upperHandle.style.left); - material.uniforms.L.value = lowerValue / 100; - material.uniforms.R.value = upperValue / 100; - }); + document.addEventListener("mousemove", (e) => { + handleMouseMove(e); + const lowerValue = parseInt(lowerHandle.style.left); + const upperValue = parseInt(upperHandle.style.left); + material.uniforms.L.value = lowerValue / 100; + material.uniforms.R.value = upperValue / 100; + }); - geometry.computeVertexNormals(); - geometry.center(); - - // Create and add the point cloud to the scene - const pointCloud = new THREE.Points(geometry, material); - scene.add(pointCloud); - - // Compute the bounding box of the point cloud - const boundingBox = new THREE.Box3().setFromObject(pointCloud); - boundingBox.getCenter(camera.position); - - // Adjust the camera position - const size = boundingBox.getSize(new THREE.Vector3()); - const maxDim = Math.max(size.x, size.y, size.z); - const fov = camera.fov * (Math.PI / 180); - let cameraZ = Math.abs(maxDim / 2 / Math.tan(fov / 2)); - camera.position.set(camera.position.x, camera.position.y, cameraZ * 1.5); - // Make the camera look at the center of the point cloud - camera.lookAt(boundingBox.getCenter(new THREE.Vector3())); - - // stats setup - let stats = new Stats(); - document.body.appendChild(stats.dom); - - // GUI setup - const gui = new GUI(); - const planeNames = ["X", "Y", "Z"]; - const planeHelpers = [ - new THREE.PlaneHelper(new THREE.Plane(), 1.3, 0xff0000), - new THREE.PlaneHelper(new THREE.Plane(), 1.3, 0x00ff00), - new THREE.PlaneHelper(new THREE.Plane(), 1.3, 0x0000ff), - ]; - planeHelpers.forEach((planeHelper) => { - scene.add(planeHelper); - planeHelper.visible = true; - }); - planes.forEach((plane, index) => { - plane.w = boundingBox.max[planeNames[index].toLowerCase()] + 1e-6; - planeHelpers[index].plane = new THREE.Plane( - new THREE.Vector3().fromArray(plane.toArray()), - -plane.w + geometry.computeVertexNormals(); + geometry.center(); + + // Create and add the point cloud to the scene + const pointCloud = new THREE.Points(geometry, material); + scene.add(pointCloud); + + const light = new THREE.HemisphereLight(0xffffff, 0x080808, 4.5); + light.position.set(-1.25, 1, 1.25); + scene.add(light); + + // Compute the bounding box of the point cloud + const boundingBox = new THREE.Box3().setFromObject(pointCloud); + boundingBox.getCenter(camera.position); + + // Adjust the camera position + const size = boundingBox.getSize(new THREE.Vector3()); + const maxDim = Math.max(size.x, size.y, size.z); + const fov = camera.fov * (Math.PI / 180); + let cameraZ = Math.abs(maxDim / 2 / Math.tan(fov / 2)); + camera.position.set( + camera.position.x, + camera.position.y, + cameraZ * 1.5 ); - - const planeFolder = gui.addFolder(`plane${planeNames[index]}`); - planeFolder.add(planeHelpers[index], "visible").name("displayHelper"); - planeFolder - .add(plane, "w") - .name("position") - .min(boundingBox.min[planeNames[index].toLowerCase()] - 1e-6) - .max(boundingBox.max[planeNames[index].toLowerCase()] + 1e-6) - .onChange((value) => { - plane.w = value; - planeHelpers[index].plane = new THREE.Plane( - new THREE.Vector3().fromArray(plane.toArray()), - -value - ); + // Make the camera look at the center of the point cloud + camera.lookAt(boundingBox.getCenter(new THREE.Vector3())); + + // GUI setup + const planeNames = ["X", "Y", "Z"]; + const planeHelpers = [ + new THREE.PlaneHelper(new THREE.Plane(), 1.3, 0xff0000), + new THREE.PlaneHelper(new THREE.Plane(), 1.3, 0x00ff00), + new THREE.PlaneHelper(new THREE.Plane(), 1.3, 0x0000ff), + ]; + planeHelpers.forEach((planeHelper) => { + scene.add(planeHelper); + planeHelper.visible = true; + }); + planes.forEach((plane, index) => { + plane.w = boundingBox.max[planeNames[index].toLowerCase()] + 1e-6; + planeHelpers[index].plane = new THREE.Plane( + new THREE.Vector3().fromArray(plane.toArray()), + -plane.w + ); + + const planeFolder = gui.addFolder(`plane${planeNames[index]}`); + planeFolder + .add(planeHelpers[index], "visible") + .name("displayHelper"); + planeFolder + .add(plane, "w") + .name("position") + .min(boundingBox.min[planeNames[index].toLowerCase()] - 1e-6) + .max(boundingBox.max[planeNames[index].toLowerCase()] + 1e-6) + .onChange((value) => { + plane.w = value; + planeHelpers[index].plane = new THREE.Plane( + new THREE.Vector3().fromArray(plane.toArray()), + -value + ); + }); + planeFolder.add({ mirror: false }, "mirror").onChange((value) => { + dir[planeNames[index].toLowerCase()] = + -dir[planeNames[index].toLowerCase()]; }); - planeFolder.add({ negated: false }, "negated").onChange((value) => { - dir[planeNames[index].toLowerCase()] = - -dir[planeNames[index].toLowerCase()]; + planeFolder.open(); + folderArray.push(planeFolder); }); - planeFolder.open(); }); +} - // Render loop - function animate() { - requestAnimationFrame(animate); - controls.update(); - stats.begin(); - renderer.render(scene, camera); - stats.end(); - } - animate(); -}); - +// stats setup +let stats = new Stats(); +document.body.appendChild(stats.dom); + +// Render loop +function animate() { + requestAnimationFrame(animate); + controls.update(); + stats.begin(); + renderer.render(scene, camera); + stats.end(); +} +animate(); // Handle window resize window.addEventListener("resize", () => { camera.aspect = window.innerWidth / window.innerHeight;