Вращение камеры вокруг центра выбранного элемента three.js
В сцене у меня есть несколько 3D объектов, объекты выделяются при клике (меняется материал) при этом задается centerPoint
- это центр выбранной геоментрии или 0,0,0. Я пытаюсь создать навигацию в сцене и вращать камеру вокруг выбранного элемента, вращение происходит при перемещении мыши и зажатых колесике мыши + Shift
. Чтобы камера вращалась вокруг точки я использовал camera.lookAt(centerPoint)
, но кажеться это не верно, потому что при выборе другого объекта и начале вращения камера перескакивает в новую позицию. Как от этого избавиться?
Можно конечно переместить камеру в новую позицию плавно, а затем начать вращение, но кажеться в 3D редакторах это реализовано по-другому, вращение идет сразу вокруг другого объекта,не могу понять как это реализовать.
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
const light = new THREE.AmbientLight(0x404040, 2);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight.castShadow = true;
scene.add(directionalLight);
scene.add(light);
directionalLight.shadow.mapSize.width = 1080;
directionalLight.shadow.mapSize.height = 1080;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 500;
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({
color: 0xf5f5f5,
flatShading: true,
metalness: 0.5
});
material.castShadow = true;
const cube = new THREE.Mesh(geometry, material);
const cube2 = new THREE.Mesh(geometry, material);
cube.receiveShadow = true;
cube.castShadow = true;
scene.add(cube);
scene.add(cube2);
cube2.position.set(5, 0, 0);
const planeGeometry = new THREE.PlaneGeometry(10, 10);
const plane = new THREE.Mesh(planeGeometry, material);
plane.rotation.x = -Math.PI / 2;
plane.position.y = -0.5;
plane.receiveShadow = true;
scene.add(plane);
var elements = [];
elements.push(plane);
elements.push(cube);
elements.push(cube2);
camera.position.z = 5;
var domelement = renderer.domElement;
var domEvents = new THREEx.DomEvents(camera, renderer.domElement);
var pan = false;
var shift = false;
let prevMouseX = 0;
let prevMouseY = 0;
var centerPoint = new THREE.Vector3(0, 0, 0);
var distance = camera.position.distanceTo(centerPoint);
domelement.addEventListener("mousedown", (event) => {
if (event.which !== 2) {
return;
}
prevMouseX = event.clientX;
prevMouseY = event.clientY;
pan = true;
});
domelement.addEventListener("mousemove", (event) => {
let moveSpeed = 0.005;
let mouseX = event.clientX;
let mouseY = event.clientY;
if (pan && !shift) {
let deltaX = (mouseX - prevMouseX) * moveSpeed;
let deltaY = (mouseY - prevMouseY) * moveSpeed;
let cameraOffset = new THREE.Vector3(-deltaX, deltaY, 0);
cameraOffset.applyQuaternion(camera.quaternion);
camera.position.add(cameraOffset);
}
if (pan && shift) {
const mouseXDelta = mouseX - prevMouseX;
const mouseYDelta = mouseY - prevMouseY;
const angleX = -(mouseXDelta / window.innerWidth) * Math.PI * 2;
const angleY = (mouseYDelta / window.innerHeight) * Math.PI * 2;
const quaternionX = new THREE.Quaternion().setFromAxisAngle(
new THREE.Vector3(0, 1, 0),
angleX
);
const quaternionY = new THREE.Quaternion().setFromAxisAngle(
new THREE.Vector3(1, 0, 0),
angleY
);
const deltaQuaternion = new THREE.Quaternion().multiplyQuaternions(
quaternionX,
quaternionY
);
const currentPosition = camera.position.clone().sub(centerPoint);
const newPosition = currentPosition.applyQuaternion(deltaQuaternion);
camera.position.copy(newPosition.add(centerPoint));
camera.lookAt(centerPoint);
}
prevMouseX = mouseX;
prevMouseY = mouseY;
});
domelement.addEventListener("mouseup", (event) => {
X = event.clientX;
Y = event.clientY;
pan = false;
});
domelement.addEventListener("mouseleave", (event) => {
X = event.clientX;
Y = event.clientY;
pan = false;
});
document.addEventListener("keydown", function(event) {
if (event.shiftKey) {
shift = true;
}
});
document.addEventListener("keyup", function(event) {
if (event.code.toString().includes("Shift")) {
shift = false;
}
});
var activeElement = null;
for (let el of elements) {
domEvents.addEventListener(
el,
"click",
function(event) {
for (let e of elements) {
e.material = material;
}
let newMaterial = new THREE.MeshStandardMaterial({
color: 0x106be3,
flatShading: true,
metalness: 0.5,
transparent: true,
opacity: 0.95
});
if (!pan && !shift) {
activeElement = event.target;
activeElement.material = newMaterial;
let middle = new THREE.Vector3();
let geometry = activeElement.geometry;
geometry.computeBoundingBox();
geometry.boundingBox.getSize(middle);
middle.x = (geometry.boundingBox.max.x + geometry.boundingBox.min.x) / 2;
middle.y = (geometry.boundingBox.max.y + geometry.boundingBox.min.y) / 2;
middle.z = (geometry.boundingBox.max.z + geometry.boundingBox.min.z) / 2;
activeElement.localToWorld(middle);
centerPoint = middle;
}
},
false
);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
<body></body>
<script src="https://rawgit.com/jeromeetienne/threex.domevents/master/threex.domevents.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.154.0/three.min.js"></script>