Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ShaneBrumback/aa9d8665166983409ca786f087c11bf6 to your computer and use it in GitHub Desktop.
Save ShaneBrumback/aa9d8665166983409ca786f087c11bf6 to your computer and use it in GitHub Desktop.
Three.js Exmaples - Selecting a 3D Cube with a Raycaster
<!--////////////////////////////////////////////////////////////////////////////////////////
/// ///
/// Example Using Three.js Library, HTML, CSS & JavaScript ///
// 3D Interactive Web Apps & Games 2021-2024 ///
/// Contact Shane Brumback https://www.shanebrumback.com ///
/// Send a message if you have questions about this code ///
/// I am a freelance developer. I develop any and all web. ///
/// Apps Websites 3D 2D CMS Systems etc. Contact me anytime :) ///
/// ///
////////////////////////////////////////////////////////////////////////////////////////////-->
<!DOCTYPE html>
<html>
<head>
<title>Three.js Examples - Selecting 3D Cube with Raycaster</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/three@latest/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@latest/examples/js/controls/OrbitControls.js"></script>
<script>
// Set up the Three.js scene
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = 5; // Move the camera further back along the x-axis
camera.position.y = 5; // Move the camera further back along the y-axis
camera.position.z = 5; // Move the camera further back along the z-axis
// Create a variable to control the rotation speed of the cube
const rotationSpeed = 0.01;
//Set up the renderer
let renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.toneMapping = THREE.ReinhardToneMapping;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.domElement.id = 'renderer';
renderer.setClearColor(0x000000, 1); // Set background color to black
renderer.domElement.style.position = 'fixed';
renderer.domElement.style.zIndex = '-1';
renderer.domElement.style.left = '0';
renderer.domElement.style.top = '0';
document.body.appendChild(renderer.domElement);
// Create an ambient light and add it to the scene
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // Soft white light with intensity 0.5
scene.add(ambientLight);
// Create a directional light and add it to the scene
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 10, 5); // Set the position of the light
scene.add(light);
// Create a mesh and add it to the scene
const geometry = new THREE.BoxGeometry();
// Set up the cube material with MeshPhongMaterial
const cubeMaterial = new THREE.MeshPhongMaterial({
color: 0x0000ff, // Blue color of the cube
specular: 0xffffff, // Specular highlight color
shininess: 100, // Shininess of the material (higher value makes it more reflective)
});
// Create a mesh with the cube geometry and the new material
const mesh = new THREE.Mesh(geometry, cubeMaterial);
scene.add(mesh);
// Add a grid to the scene
const gridHelper = new THREE.GridHelper(10, 10);
scene.add(gridHelper);
// Set up OrbitControls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
// Create a variable to track the selected state of the cube
let isCubeSelected = false;
// Create variables to store the starting position and target position of the cube
const startingPosition = mesh.position.clone();
const maxDistance = 2; // Maximum distance from the camera to stop the movement
// Store the initial position of the cube for returning
const initialPosition = mesh.position.clone();
// Add event listener for mouse clicks
document.addEventListener('click', function (event) {
// Calculate mouse position in normalized device coordinates
const mouse = new THREE.Vector2();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// Raycasting from camera to object
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObject(mesh);
if (intersects.length > 0) {
// If the cube was previously selected, move it back to the absolute position (0, 0, 0)
if (isCubeSelected) {
isCubeSelected = false;
moveCube(initialPosition);
// Set the cube's color back to green
mesh.material.color.set(0x0000ff);
} else {
// If the cube was not previously selected, move it towards the camera
isCubeSelected = true;
const targetPosition = camera.position.clone();
if (mesh.position.distanceTo(targetPosition) <= maxDistance) {
// If the cube is already close to the camera, move it back to the absolute position (0, 0, 0)
moveCube(initialPosition);
// Set the cube's color back to blue
mesh.material.color.set(0x0000ff);
} else {
// Move the cube to the target position
moveCube(targetPosition);
// Set the cube's color to green when it's selected
mesh.material.color.set(0x00ff00,);
}
}
}
});
// Function to move the cube to the specified position
function moveCube(position) {
const duration = 1; // Duration of the movement in seconds
const startPosition = mesh.position.clone();
let elapsed = 0;
function updatePosition() {
elapsed += 0.016; // Assuming 60 FPS, use delta time for more accurate timing
const t = elapsed / duration;
if (t < 1) {
const currentPosition = mesh.position.clone();
const distanceToTarget = currentPosition.distanceTo(position);
// Stop moving if the distance to the target position is within the specified range
if (distanceToTarget <= 2) { // Check if the distance is less than or equal to 2 units
mesh.position.copy(currentPosition);
return;
}
mesh.position.lerpVectors(startPosition, position, t);
requestAnimationFrame(updatePosition);
} else {
mesh.position.copy(position);
// Check if the cube has reached the target position (center of the scene)
if (position.equals(initialPosition)) {
isCubeSelected = false;
controls.target.set(0, 0, 0); // Reset the controls target to the scene's origin
}
}
}
updatePosition();
}
// Function to calculate the center of the cube's bounding box
function getBoundingBoxCenter(mesh) {
const box = new THREE.Box3().setFromObject(mesh);
const center = new THREE.Vector3();
box.getCenter(center);
return center;
}
// Render loop
function animate() {
requestAnimationFrame(animate);
// Rotate the cube around its y-axis
mesh.rotation.y += rotationSpeed;
// Set the OrbitControls center to the center of the cube's bounding box
const center = getBoundingBoxCenter(mesh);
controls.target.copy(center);
renderer.render(scene, camera);
}
animate();
// Adjust the camera aspect ratio and update renderer size on window resize
window.addEventListener("resize", onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment