body {
margin: 0;
padding: 0;
}
import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.122/build/three.module.js";
import { OrbitControls } from "https://cdn.jsdelivr.net/npm/three@0.122/examples/jsm/controls/OrbitControls.js";
var camera, controls, scene, renderer, cubes, tween;
var objects = [];
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(
50,
window.innerWidth / window.innerHeight,
0.01,
100000
);
camera.position.z = 10;
camera.position.y = 5;
scene = new THREE.Scene();
buildScene();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.5;
controls.addEventListener('end', () => {
updateCameraOrbit();
});
updateCameraOrbit();
renderer.domElement.addEventListener("click", onclick, true);
}
function onclick(event) {
var selectedObject;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(cubes, true);
if (intersects.length > 0) {
selectedObject = intersects[0];
//Todo: Calculate a point that is 5 units nearer to the camera than the target position
// so that the camera stops right in front of each clicked pillar
var cameraPosition = camera.position.clone();
console.log("cameraPosition : ", logVector3(cameraPosition));
var targetPosition = selectedObject.object.position.clone();
console.log("targetPosition : ", logVector3(targetPosition));
var distance = cameraPosition.sub(targetPosition);
console.log("distance : ", logVector3(distance));
var direction = distance.normalize();
console.log("direction : ", logVector3(direction));
var offset = distance.clone().sub(direction.multiplyScalar(10.0));
console.log("offset : ", logVector3(offset));
var newPos = selectedObject.object.position.clone().sub(offset);
newPos.y = camera.position.y;
tween = new TWEEN.Tween(camera.position).to(
{
x: newPos.x,
y: newPos.y,
z: newPos.z
},
2000
);
tween.easing(TWEEN.Easing.Quadratic.Out);
tween.start();
tween.onUpdate(function () {
updateCameraOrbit();
}.bind(this));
tween.onComplete(function () {
updateCameraOrbit();
}.bind(this));
}
}
function updateCameraOrbit() {
// Update OrbitControls target to a point just in front of the camera
var forward = new THREE.Vector3();
camera.getWorldDirection(forward);
controls.target.copy(camera.position).add(forward);
};
function animate() {
TWEEN.update();
requestAnimationFrame(animate);
renderer.render(scene, camera);
controls.update();
}
function buildScene() {
var planeGeometry = new THREE.PlaneGeometry(100, 100, 50, 50);
var material = new THREE.MeshBasicMaterial({
color: 0xffffff,
wireframe: true
});
var plane = new THREE.Mesh(planeGeometry, material);
plane.rotateX(-Math.PI / 2);
scene.add(plane);
cubes = [];
var mat, geo;
for (var i = 0; i < 30; i++) {
geo = new THREE.BoxGeometry(
Math.random() * 4,
Math.random() * 16,
Math.random() * 4
);
mat = new THREE.MeshBasicMaterial({ wireframe: false });
switch (i % 3) {
case 0:
mat.color = new THREE.Color(0xff0000);
break;
case 1:
mat.color = new THREE.Color(0xffff00);
break;
case 2:
mat.color = new THREE.Color(0x0000ff);
break;
}
const cube = new THREE.Mesh(geo, mat);
cubes.push(cube);
}
cubes.forEach((c) => {
c.position.x = Math.random() * 100 - 50;
c.position.z = Math.random() * 100 - 50;
c.geometry.computeBoundingBox();
c.position.y =
(c.geometry.boundingBox.max.y - c.geometry.boundingBox.min.y) / 2;
scene.add(c);
});
}
function logVector3(pos){
if(pos.x || pos.x === 0){
return "x: " + pos.x + " / y: " + pos.y + " / z: " + pos.z;
}else{
return pos;
}
}
This Pen doesn't use any external CSS resources.