There is a regular BoxGeometry whose dimensions are set relative to a group of objects.
How to get the position of a certain point in this BoxGeometry (x:1, y:1, z:1), relative to the world.
I wrote such a function.
const getBoxVertexPos = (box, side) => {
box = new THREE.Box3().setFromObject(box);
let size = box.getSize(new THREE.Vector3()),
center = box.getCenter(new THREE.Vector3());
return {
x: side[0] > 0 ? (center.x + (size.x / 2)) : (center.x - (size.x / 2)),
y: side[1] > 0 ? (center.y + (size.y / 2)) : (center.y - (size.y / 2)),
z: side[2] > 0 ? (center.z + (size.z / 2)) : (center.z - (size.z / 2)),
}
};
In general, the function does what I need, but something tells me that there is a better way.
let canvas = document.querySelector('canvas');
let width, height;
const canvasSize = () => {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
}
canvasSize();
let renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true});
let scene = new THREE.Scene();
renderer.setSize(width, height);
let camera = new THREE.PerspectiveCamera(55, width / height, .1, 15);
camera.position.set(3, 3, 3);
camera.aspect = width / height;
// CUBE
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({color: 0x00ff00});
const cube = new THREE.Mesh(geometry, material);
cube.position.x = .5;
scene.add(cube)
cube.visible = false;
// HELPERS
const boxHelper = new THREE.BoxHelper(cube, 0xffff00);
scene.add(boxHelper);
const axesHelper = new THREE.AxesHelper(2);
scene.add(axesHelper);
const getBoxVertexPos = (box, side) => {
box = new THREE.Box3().setFromObject(box);
let size = box.getSize(new THREE.Vector3()),
center = box.getCenter(new THREE.Vector3());
return {
x: side[0] > 0 ? (center.x + (size.x / 2)) : (center.x - (size.x / 2)),
y: side[1] > 0 ? (center.y + (size.y / 2)) : (center.y - (size.y / 2)),
z: side[2] > 0 ? (center.z + (size.z / 2)) : (center.z - (size.z / 2)),
}
};
let A = getBoxVertexPos(cube, [1, 1, 1]);
const geometryA = new THREE.BoxGeometry(.1,.1,.1);
const materialA = new THREE.MeshBasicMaterial({color: 0x00ff00});
const cubeA = new THREE.Mesh(geometryA, materialA);
cubeA.position.x = A.x;
cubeA.position.y = A.y;
cubeA.position.z = A.z;
scene.add(cubeA);
let controls = new THREE.OrbitControls(camera, renderer.domElement);
const render = () => {
renderer.render(scene, camera);
}
render();
controls.addEventListener('change', render, false);
const resize = () => {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
render()
}
resize();
window.addEventListener('resize', resize);
body {margin: 0; overflow: hidden;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<canvas></canvas>
Related
Structure of my project 66% threejs model, 44% html (side control) using Bootstrap.
I’m trying to make mouse picker, when pointing at an object so that it is shown on which object it is pointed. As I understand it, he sees the coordination of the mouse badly.
Please help me figure out and set up the correct coordination mouse with Canvas.
Project Structure Screenshot:
Code:
export class CustomizerComponent implements OnInit {
public textureSrc: string;
public textureList = [
{ id: 1, name: 'Wood 2', src: '/assets/textures/wood2.jpg' },
{ id: 2, name: 'Wood 1', src: '/assets/textures/wood1.jpg' },
];
public selectedTexture(e): void {
let find = this.textureList.find((x) => x?.src === e.target.value);
// console.log(find?.src);
}
ngOnInit(): void {
let scene, kitchen, camera, renderer, canvas, controls, mouse, raycaster, loader, BACKGROUND_COLOR = 0xffffff;
// Scene
scene = new THREE.Scene();
scene.background = new THREE.Color(BACKGROUND_COLOR);
// Renderer with canvas
canvas = document.getElementById('canvas');
renderer = new THREE.WebGLRenderer({ canvas: canvas });
renderer.setSize(canvas.width * 3.5, canvas.height * 5);
console.log(canvas.clientWidth);
renderer.shadowMap.enabled = true;
// Set Camera
camera = new THREE.PerspectiveCamera(
75,
canvas.clientWidth / canvas.clientHeight,
0.1,
1000
);
camera.position.z = 5;
camera.position.x = 5;
camera.position.y = 1;
mouse = new THREE.Vector2();
raycaster = new THREE.Raycaster();
// Controls with options
controls = new OrbitControls(camera, renderer.domElement);
controls.maxPolarAngle = Math.PI / 2;
controls.minPolarAngle = Math.PI / 3;
controls.enableDamping = true;
controls.enablePan = false;
controls.target.set(5, 1, 0);
controls.maxAzimuthAngle = 0.8;
controls.minAzimuthAngle = -0.8;
controls.enableZoom = false;
controls.dampingFactor = 0.1;
controls.autoRotate = false;
controls.autoRotateSpeed = 0.2; // 30
loader = new GLTFLoader();
loader.load(
'assets/3d_models/kitch.glb',
(gltf) => {
kitchen = gltf.scene;
scene.add(kitchen);
console.log(kitchen);
},
undefined,
(err) => {
console.log(err);
}
);
function hoverPieces() {
const material = new THREE.MeshBasicMaterial({ color: 0xffff00 });
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
// console.log(intersects);
for (let i = 0; i < intersects.length; i++) {
intersects[i].object.material = material;
}
console.log(intersects);
}
// Set Lights
setLights(scene);
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
hoverPieces();
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
}
animate();
function onMouseMove(event) {
// calculate pointer position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = ((event.clientX - 250) / canvas.clientWidth) * 2 - 1;
mouse.y = -(event.clientY / canvas.clientHeight) * 2 + 1;
}
window.addEventListener('mousemove', onMouseMove);
}
}
function setLights(scene) {
// Add lights
var hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.61);
hemiLight.name = 'hemiLight';
hemiLight.position.set(0, 50, 0);
// Add hemisphere light to scene
scene.add(hemiLight);
var dirLight = new THREE.DirectionalLight(0xffffff, 0.54);
dirLight.name = 'dirLight';
dirLight.position.set(-8, 12, 8);
// dirLight.castShadow = true;
dirLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
// Add directional Light to scene
scene.add(dirLight);
var light = new THREE.DirectionalLight(0xffffff, 3);
light.name = 'light';
light.position.set(10, 10, 10);
scene.add(light);
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
var width = canvas.clientWidth;
var height = canvas.clientHeight;
var canvasPixelWidth = canvas.clientWidth / window.devicePixelRatio;
var canvasPixelHeight = canvas.clientHeight / window.devicePixelRatio;
const needResize = canvasPixelWidth !== width || canvasPixelHeight !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
Gist link :
https://gist.github.com/artur33s/e15abda28707231863ded08ccfe0887d
I suggest you use pointermove instead of mousemove and also use getBoundingClientRect() in your event listener:
function onPointerMove(event) {
const rect = renderer.domElement.getBoundingClientRect();
mouse.x = ( ( event.clientX - rect.left ) / ( rect.right - rect.left ) ) * 2 - 1;
mouse.y = - ( ( event.clientY - rect.top ) / ( rect.bottom - rect.top) ) * 2 + 1;
}
window.addEventListener('pointermove', onPointerMove);
I'm pulling my hair off this one. I have to work with a specific camera angle for a 3D projet with the constraint of using an Orthographic Camera. I need to be able to precisely click on the floor for gameplay purposes. The ThreeJS Raycast doesn't seem to work properly (or maybe I set something the wrong way?). In a top-down view like angle, it works better.
Here is a fiddle that explains the kind of situation I'm in: https://jsfiddle.net/p6td5oak/42/
const sceneWidth = window.innerWidth;
const sceneHeight = window.innerHeight;
const scene = new THREE.Scene();
const camera = new THREE.OrthographicCamera( -sceneWidth / 2, sceneWidth / 2, sceneHeight / 2, -sceneHeight / 2, -1000, 1000 );
camera.rotation.set(
-Math.PI / 12,
Math.PI / 12,
Math.PI / 24
);
camera.position.set(0, 1, 0);
camera.zoom = 2;
camera.updateProjectionMatrix();
const renderer = new THREE.WebGLRenderer();
renderer.setSize( sceneWidth, sceneHeight );
document.body.appendChild( renderer.domElement );
const whiteMaterial = new THREE.MeshBasicMaterial({});
const redMaterial = new THREE.MeshBasicMaterial({
color: 0xFF0000
});
const size = 100;
const geometry = new THREE.PlaneGeometry(size, size, 10, 10);
for (var x = 0; x < 2; x++)
{
for (var z = 0; z < 2; z++)
{
let mesh = new THREE.Mesh(geometry, ((x + z) % 2 ? whiteMaterial : redMaterial));
mesh.rotation.set(
-Math.PI / 2,
0,
0
);
mesh.position.set(
x*size,
0,
z*size
)
scene.add(mesh);
}
}
var raycaster = new THREE.Raycaster();
window.addEventListener("pointerup", function(e)
{
var screenPos = new THREE.Vector2();
screenPos.x = (e.clientX / window.innerWidth) * 2 - 1;
screenPos.y = - (e.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(screenPos, camera);
var rays = raycaster.intersectObjects(scene.children, true);
for (var i = 0; i < rays.length; i++)
{
scene.remove(rays[i].object);
}
}
.bind(this));
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
In the example, I try to remove the planes as soon as they are clicked. As you can see, the top two planes can be removed if you click around their top-left corner. The other twos cannot even be triggered.
If someone have an idea what's going on, you'll be my hero.
Thanks!
PS: I have basic knowledge of ThreeJS but I'm far from being expert
Raycaster only detects objects in front of the camera, and your camera is located near the origin. Move the camera back.
Also, the near value of your orthographic camera is invalid. From the documentation:
The valid range is between 0 and the current value of the far plane.
Negative values are not supported.
const sceneWidth = window.innerWidth;
const sceneHeight = window.innerHeight;
const scene = new THREE.Scene();
const camera = new THREE.OrthographicCamera(-sceneWidth / 2, sceneWidth / 2, sceneHeight / 2, -sceneHeight / 2, 0.1, 1000);
camera.rotation.set(
-Math.PI / 12,
Math.PI / 12,
Math.PI / 24
);
camera.position.set(100, 100, 500);
camera.zoom = 2;
camera.updateProjectionMatrix();
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(sceneWidth, sceneHeight);
document.body.appendChild(renderer.domElement);
const whiteMaterial = new THREE.MeshBasicMaterial();
const redMaterial = new THREE.MeshBasicMaterial({
color: 0xFF0000
});
const size = 100;
const geometry = new THREE.PlaneGeometry(size, size, 10, 10);
for (let x = 0; x < 2; x++) {
for (let z = 0; z < 2; z++) {
let mesh = new THREE.Mesh(geometry, ((x + z) % 2 ? whiteMaterial : redMaterial));
mesh.rotation.set(
-Math.PI / 2,
0,
0
);
mesh.position.set(
x * size,
0,
z * size
)
scene.add(mesh);
}
}
const raycaster = new THREE.Raycaster();
const screenPos = new THREE.Vector2();
renderer.domElement.addEventListener("pointerup", function(e) {
screenPos.x = (e.clientX / window.innerWidth) * 2 - 1;
screenPos.y = -(e.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(screenPos, camera);
const intersections = raycaster.intersectObject(scene, true);
for (let i = 0; i < intersections.length; i++) {
scene.remove(intersections[i].object);
}
});
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.129/build/three.js"></script>
I want to show lines only on the edges. Here I have included my output model which I tried using edgeGeometry and LinebasicMaterial. I want to remove the inner edge lines and show only outline edges
You can use EdgesGeometry
You pass it some other geometry and a threshold angle
// only show edges with 15 degrees or more angle between faces
const thresholdAngle = 15;
const lineGeometry = new THREE.EdgesGeometry(geometry, thresholdAngle));
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 20;
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xAAAAAA);
let solidMesh;
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(1, -2, -4);
scene.add(light);
}
const objects = [];
const spread = 15;
function addObject(x, y, obj) {
obj.position.x = x * spread;
obj.position.y = y * spread;
scene.add(obj);
objects.push(obj);
}
function createMaterial() {
const material = new THREE.MeshPhongMaterial({
side: THREE.DoubleSide,
});
const hue = Math.random();
const saturation = 1;
const luminance = .5;
material.color.setHSL(hue, saturation, luminance);
return material;
}
function addSolidGeometry(x, y, geometry) {
const mesh = new THREE.Mesh(geometry, createMaterial());
addObject(x, y, mesh);
return mesh;
}
function addLineGeometry(x, y, geometry) {
const material = new THREE.LineBasicMaterial({color: 0x000000});
const mesh = new THREE.LineSegments(geometry, material);
addObject(x, y, mesh);
return mesh;
}
{
const shape = new THREE.Shape();
const x = -2.5;
const y = -5;
shape.moveTo(x + 2.5, y + 2.5);
shape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);
shape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);
shape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5);
shape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);
shape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);
shape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);
const extrudeSettings = {
steps: 2,
depth: 2,
bevelEnabled: true,
bevelThickness: 1,
bevelSize: 1,
bevelSegments: 2,
};
const geometry = new THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
solidMesh = addSolidGeometry(0, 0, geometry);
const thresholdAngle = 15;
addLineGeometry(0, 0, new THREE.EdgesGeometry(geometry, thresholdAngle));
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
solidMesh.visible = (time | 0) % 2 !== 0;
objects.forEach((obj, ndx) => {
const speed = .1 + ndx * .0;
const rot = time * speed;
obj.rotation.x = rot;
obj.rotation.y = rot;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r108/build/three.min.js"></script>
Here I have created a rect in which it gets zoom in to the mid point of the scene. Here I'm looking to zoom in to the center of the rect (where we select the object) towards the object.
I have an idea to get the center point of the rect and and pass it to the zoom function where I'm struggling in.
Please help me out
Heres the snippet
/**
* #author HypnosNova / https://www.threejs.org.cn/gallery
* This is a class to check whether objects are in a selection area in 3D space
*/
THREE.SelectionBox = (function() {
var frustum = new THREE.Frustum();
var center = new THREE.Vector3();
function SelectionBox(camera, scene, deep) {
this.camera = camera;
this.scene = scene;
this.startPoint = new THREE.Vector3();
this.endPoint = new THREE.Vector3();
this.collection = [];
this.deep = deep || Number.MAX_VALUE;
}
SelectionBox.prototype.select = function(startPoint, endPoint) {
this.startPoint = startPoint || this.startPoint;
this.endPoint = endPoint || this.endPoint;
this.collection = [];
this.updateFrustum(this.startPoint, this.endPoint);
this.searchChildInFrustum(frustum, this.scene);
return this.collection;
};
SelectionBox.prototype.updateFrustum = function(startPoint, endPoint) {
startPoint = startPoint || this.startPoint;
endPoint = endPoint || this.endPoint;
this.camera.updateProjectionMatrix();
this.camera.updateMatrixWorld();
var tmpPoint = startPoint.clone();
tmpPoint.x = Math.min(startPoint.x, endPoint.x);
tmpPoint.y = Math.max(startPoint.y, endPoint.y);
endPoint.x = Math.max(startPoint.x, endPoint.x);
endPoint.y = Math.min(startPoint.y, endPoint.y);
var vecNear = this.camera.position.clone();
var vecTopLeft = tmpPoint.clone();
var vecTopRight = new THREE.Vector3(endPoint.x, tmpPoint.y, 0);
var vecDownRight = endPoint.clone();
var vecDownLeft = new THREE.Vector3(tmpPoint.x, endPoint.y, 0);
vecTopLeft.unproject(this.camera);
vecTopRight.unproject(this.camera);
vecDownRight.unproject(this.camera);
vecDownLeft.unproject(this.camera);
var vectemp1 = vecTopLeft.clone().sub(vecNear);
var vectemp2 = vecTopRight.clone().sub(vecNear);
var vectemp3 = vecDownRight.clone().sub(vecNear);
vectemp1.normalize();
vectemp2.normalize();
vectemp3.normalize();
vectemp1.multiplyScalar(this.deep);
vectemp2.multiplyScalar(this.deep);
vectemp3.multiplyScalar(this.deep);
vectemp1.add(vecNear);
vectemp2.add(vecNear);
vectemp3.add(vecNear);
var planes = frustum.planes;
planes[0].setFromCoplanarPoints(vecNear, vecTopLeft, vecTopRight);
planes[1].setFromCoplanarPoints(vecNear, vecTopRight, vecDownRight);
planes[2].setFromCoplanarPoints(vecDownRight, vecDownLeft, vecNear);
planes[3].setFromCoplanarPoints(vecDownLeft, vecTopLeft, vecNear);
planes[4].setFromCoplanarPoints(vecTopRight, vecDownRight, vecDownLeft);
planes[5].setFromCoplanarPoints(vectemp3, vectemp2, vectemp1);
planes[5].normal.multiplyScalar(-1);
};
SelectionBox.prototype.searchChildInFrustum = function(frustum, object) {
if (object.isMesh) {
if (object.material !== undefined) {
object.geometry.computeBoundingSphere();
center.copy(object.geometry.boundingSphere.center);
center.applyMatrix4(object.matrixWorld);
if (frustum.containsPoint(center)) {
this.collection.push(object);
}
}
}
if (object.children.length > 0) {
for (var x = 0; x < object.children.length; x++) {
this.searchChildInFrustum(frustum, object.children[x]);
}
}
};
return SelectionBox;
})();
/**
* #author HypnosNova / https://www.threejs.org.cn/gallery
*/
THREE.SelectionHelper = (function() {
function SelectionHelper(selectionBox, renderer, cssClassName) {
this.element = document.createElement('div');
this.element.classList.add(cssClassName);
this.element.style.pointerEvents = 'none';
this.renderer = renderer;
this.startPoint = new THREE.Vector2();
this.pointTopLeft = new THREE.Vector2();
this.pointBottomRight = new THREE.Vector2();
this.isDown = false;
this.renderer.domElement.addEventListener('mousedown', function(event) {
this.isDown = true;
this.onSelectStart(event);
}.bind(this), false);
this.renderer.domElement.addEventListener('mousemove', function(event) {
if (this.isDown) {
this.onSelectMove(event);
}
}.bind(this), false);
this.renderer.domElement.addEventListener('mouseup', function(event) {
this.isDown = false;
this.onSelectOver(event);
}.bind(this), false);
}
SelectionHelper.prototype.onSelectStart = function(event) {
this.renderer.domElement.parentElement.appendChild(this.element);
this.element.style.left = event.clientX + 'px';
this.element.style.top = event.clientY + 'px';
this.element.style.width = '0px';
this.element.style.height = '0px';
this.startPoint.x = event.clientX;
this.startPoint.y = event.clientY;
};
SelectionHelper.prototype.onSelectMove = function(event) {
this.pointBottomRight.x = Math.max(this.startPoint.x, event.clientX);
this.pointBottomRight.y = Math.max(this.startPoint.y, event.clientY);
this.pointTopLeft.x = Math.min(this.startPoint.x, event.clientX);
this.pointTopLeft.y = Math.min(this.startPoint.y, event.clientY);
this.element.style.left = this.pointTopLeft.x + 'px';
this.element.style.top = this.pointTopLeft.y + 'px';
this.element.style.width = (this.pointBottomRight.x - this.pointTopLeft.x) + 'px';
this.element.style.height = (this.pointBottomRight.y - this.pointTopLeft.y) + 'px';
};
SelectionHelper.prototype.onSelectOver = function() {
this.element.parentElement.removeChild(this.element);
};
return SelectionHelper;
})();
var container;
var camera, scene, renderer;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 5000);
camera.position.z = 1000;
scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f0f0);
scene.add(new THREE.AmbientLight(0x505050));
var light = new THREE.SpotLight(0xffffff, 1.5);
light.position.set(0, 500, 2000);
light.angle = Math.PI / 9;
light.castShadow = true;
light.shadow.camera.near = 1000;
light.shadow.camera.far = 4000;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
scene.add(light);
var geometry = new THREE.BoxBufferGeometry(20, 20, 20);
for (var i = 0; i < 200; i++) {
var object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: Math.random() * 0xffffff
}));
object.position.x = Math.random() * 1600 - 800;
object.position.y = Math.random() * 900 - 450;
object.position.z = Math.random() * 900 - 500;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
object.scale.x = Math.random() * 2 + 1;
object.scale.y = Math.random() * 2 + 1;
object.scale.z = Math.random() * 2 + 1;
object.castShadow = true;
object.receiveShadow = true;
scene.add(object);
}
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFShadowMap;
container.appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
var selectionBox = new THREE.SelectionBox(camera, scene);
var helper = new THREE.SelectionHelper(selectionBox, renderer, 'selectBox');
document.addEventListener('mousedown', function(event) {
for (var item of selectionBox.collection) {
item.material.emissive = new THREE.Color(0x000000);
}
selectionBox.startPoint.set(
(event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1,
0.5);
});
document.addEventListener('mousemove', function(event) {
if (helper.isDown) {
for (var i = 0; i < selectionBox.collection.length; i++) {
selectionBox.collection[i].material.emissive = new THREE.Color(0x000000);
}
selectionBox.endPoint.set(
(event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1,
0.5);
var allSelected = selectionBox.select();
for (var i = 0; i < allSelected.length; i++) {
allSelected[i].material.emissive = new THREE.Color(0x0000ff);
}
}
});
document.addEventListener('mouseup', function(event) {
selectionBox.endPoint.set(
(event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1,
0.5);
var allSelected = selectionBox.select();
for (var i = 0; i < allSelected.length; i++) {
allSelected[i].material.emissive = new THREE.Color(0x0000ff);
}
});
body {
background-color: #f0f0f0;
color: #000;
margin: 0;
}
canvas {
display: block;
}
a {
color: #08e;
}
.selectBox {
border: 1px solid #55aaff;
background-color: rgba(75, 160, 255, 0.3);
position: fixed;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.min.js"></script>
You want to zoom in to the selected area? Based on what you're doing, you could try something like this:
function zoomToSelection() {
topLeft = helper.pointTopLeft;
bottomRight = helper.pointBottomRight;
// Get the centerpoint of the selection.
let center = new THREE.Vector2(topLeft.x + (bottomRight.x - topLeft.x) / 2, topLeft.y + (bottomRight.y - topLeft.y) / 2);
// Get the center position in world space.
var vector = new THREE.Vector3(
(center.x / window.innerWidth) * 2 - 1,
-(center.y / window.innerHeight) * 2 + 1,
0.5
);
vector.unproject(camera);
camera.lookAt(vector);
var movement = vector.clone();
// Get the ratio between the box size and the window.
let zoomNeeded = (bottomRight.y - topLeft.y) / window.innerHeight;
// Get a scalar by which to move the camera in the direction it's looking.
let distanceToOrigin = camera.position.distanceTo(new THREE.Vector3(0, 0, 0))
let distance = distanceToOrigin - distanceToOrigin * zoomNeeded;
movement.sub(camera.position).normalize().multiplyScalar(-zoom);
var toDirection = camera.position.clone().sub(movement);
camera.position.set(toDirection.x, toDirection.y, toDirection.z);
}
Now just add to your 'mouseup' event listener:
document.addEventListener('mouseup', function(event) {
//...
zoomToSelection();
});
Hi i am having a problem maybe you can help me.
I have a camera that is going down a tube following a path. and a camera that rotates around that tube always pointing toward the next point in the tube. However, the camera sometimes can be below or beside the tube like a roller coaster. Like this
I have the position of point a and the position of the camera which is point b. I am always looking at point a+1
var bpoints = this.cameraPathpoints;
var apoints = this.pathPoints;
this.camera.position.copy(bpoints[i]);
this.camera.lookAt(apoints[i+1]);
The camera is always looking at the point correctly however i want that the camera rotates in its z axis so that it is always normal to the tube. I tried making some calculations so that the camera rotates in its z axis so that the camera always faces normal to the tube, however my calculations work only on certain positions. Maybe there is a simpler way to do this. Thank you very much for any help.
var angleRadians = Math.atan2(cpv[this.cameraPos].pos.y - centePoints[this.cameraPos].pos.y, cpv[this.cameraPos].pos.x - centePoints[this.cameraPos].pos.x);
if(angleRadians > 0 && angleRadians > Math.PI/2){
console.log("+90",(Math.PI/2) - angleRadians);
angleRadians = (Math.PI/2) - angleRadians;
this.camera.rotateZ(angleRadians);
console.log("rotated ", angleRadians * 180/Math.PI);
}
else if(angleRadians > 0 && angleRadians < Math.PI/2 && anglesum >
Math.PI/2){
console.log("-90",(Math.PI/2) - angleRadians);
angleRadians = (Math.PI/2) - angleRadians;
this.camera.rotateZ(-angleRadians);
console.log("rotated ", -angleRadians * 180/Math.PI);
}
else if(angleRadians > 0 && angleRadians < Math.PI/2){
console.log("-90",(Math.PI/2) + angleRadians);
angleRadians = -(Math.PI/2) - (angleRadians/Math.PI/2);
this.camera.rotateZ(angleRadians);
console.log("rotated ", angleRadians * 180/Math.PI);
}
else if(angleRadians < 0 && angleRadians < -Math.PI/2){
console.log("--90");
angleRadians = (Math.PI/2) + angleRadians;
this.camera.rotateZ(-angleRadians);
console.log("rotated ",-angleRadians * 180/Math.PI);
}else if(angleRadians < 0 && angleRadians > -Math.PI/2){
console.log("+-90");
angleRadians = (Math.PI/2) - angleRadians;
this.camera.rotateZ(-angleRadians);
console.log("rotated ", -angleRadians * 180/Math.PI);
}
Rather than doing math, make the camera a child of some other THREE.Object3D and use lookAt with that object. Set the camera's position and rotation relative to that object.
Below the object is called the mount. It goes down the path (center of the tube). The camera is a child of mount. The tube has a 1 unit radius so setting the camera.position.y to 1.5 makes it outside the tube. lookAt makes non-camera objects look down positive Z but the camera looks down negative Z so we rotate the camera 180 degrees.
Example:
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas: canvas});
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xAAAAAA);
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.y = 1.5; // 2 units above the mount
camera.rotation.y = Math.PI; // the mount will lootAt positiveZ
const mount = new THREE.Object3D();
mount.add(camera);
scene.add(mount);
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(1, -2, -4);
scene.add(light);
}
const curve = new THREE.Curves.GrannyKnot();
const tubularSegments = 200;
const radius = 1;
const radialSegments = 6;
const closed = true;
const tube = new THREE.TubeBufferGeometry(
curve, tubularSegments, radius, radialSegments, closed);
const texture = new THREE.DataTexture(new Uint8Array([128, 255, 255, 128]),
2, 2, THREE.LuminanceFormat);
texture.needsUpdate = true;
texture.magFilter = THREE.NearestFilter;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 100, 4 );
const material = new THREE.MeshPhongMaterial({
map: texture,
color: '#8CF',
flatShading: true,
});
const mesh = new THREE.Mesh(tube, material);
scene.add(mesh);
const target = new THREE.Vector3();
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
const t = time * 0.1 % 1;
curve.getPointAt(t, mount.position);
curve.getPointAt((t + 0.01) % 1, target);
mount.lookAt(target);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/js/CurveExtras.js"></script>
You can easily orient the camera relative to the mount to say look more toward the path or way by setting camera.rotation.x. If you want to rotate around the mount either change the mount's up property or add another object between the mount and the camera and set its Z rotation.
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas: canvas});
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xAAAAAA);
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.y = 1.5; // 2 units above the mount
camera.rotation.y = Math.PI; // the mount will lootAt positiveZ
const mount = new THREE.Object3D();
const subMount = new THREE.Object3D();
subMount.rotation.z = Math.PI * .5;
subMount.add(camera);
mount.add(subMount);
scene.add(mount);
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(1, -2, -4);
scene.add(light);
}
const curve = new THREE.Curves.GrannyKnot();
const tubularSegments = 200;
const radius = 1;
const radialSegments = 6;
const closed = true;
const tube = new THREE.TubeBufferGeometry(
curve, tubularSegments, radius, radialSegments, closed);
const texture = new THREE.DataTexture(new Uint8Array([128, 255, 255, 128]),
2, 2, THREE.LuminanceFormat);
texture.needsUpdate = true;
texture.magFilter = THREE.NearestFilter;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 100, 4 );
const material = new THREE.MeshPhongMaterial({
map: texture,
color: '#8CF',
flatShading: true,
});
const mesh = new THREE.Mesh(tube, material);
scene.add(mesh);
const target = new THREE.Vector3();
const target2 = new THREE.Vector3();
const mountToTarget = new THREE.Vector3();
const targetToTarget2 = new THREE.Vector3();
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
const t = time * 0.1 % 1;
curve.getPointAt(t, mount.position);
curve.getPointAt((t + 0.01) % 1, target);
// set mount up to be perpenticular to the
// curve
curve.getPointAt((t + 0.02) % 1, target2);
mountToTarget.subVectors(mount.position, target).normalize();
targetToTarget2.subVectors(target2, target).normalize();
mount.up.crossVectors(mountToTarget, targetToTarget2);
mount.lookAt(target);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/js/CurveExtras.js"></script>