I am creating a scene using Three.js where I can move objects around. I used the OBJMTLLoader to load in the objects. However, these objects don't have a local origin, they still store their original origins. When I go to move an object, the object first moves back to its original origin, and then you can move the object from there. I have found lots of examples of using centroids and bounding boxes, but I always get that something is undefined. Any help would be greatly appreciated!!
loader.load('example.obj', 'example.mtl', function (object) {
object.position.y = -50; // I want this to be the new origin of the object
object.scale.set(scale, scale, scale);
});
And then my code to move the object:
function onDocumentMouseMove(event) {
event.preventDefault();
mouse.x = ( (event.clientX - container.offsetLeft) / container.clientWidth ) * 2 - 1;
mouse.y = -( (event.clientY - container.offsetTop ) / container.clientHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
if (SELECTED) {
var intersects = raycaster.intersectObject(plane);
SELECTED.position.copy(intersects[0].point.sub(offset));
return;
}
var intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
if (INTERSECTED != intersects[0].object) {
INTERSECTED = intersects[0].object;
plane.position.copy(INTERSECTED.position);
plane.lookAt(camera.position);
updateLightPosition();
}
container.style.cursor = 'pointer';
} else {
INTERSECTED = null;
container.style.cursor = 'auto';
}
}
function onDocumentMouseDown(event) {
event.preventDefault();
var vector = new THREE.Vector3(mouse.x, mouse.y, 1).unproject(camera);
raycaster.set(camera.position, vector.sub(camera.position).normalize());
if (selectingTargetPos) {
var intersects = raycaster.intersectObjects(background);
if (intersects.length > 0) {
selectedObject.light.target = new THREE.Object3D();
selectedObject.light.target.position.copy(intersects[0].point);
selectedObject.light.target.updateMatrixWorld();
selectingTargetPos = false;
container.style.cursor = 'auto';
}
} else if (selectingTargetObj) {
var intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
selectedObject.light.target = intersects[0].object;
selectedObject.light.target.updateMatrixWorld();
selectingTargetObj = false;
container.style.cursor = 'auto';
}
} else {
var intersects = raycaster.intersectObjects(objects, true);
if (intersects.length > 0) {
SELECTED = intersects[0].object.userData.rootObject;
selectedObject = SELECTED;
var intersects = raycaster.intersectObject(plane);
offset.copy(intersects[0].point).sub(plane.position);
container.style.cursor = 'move';
}
}
}
function onDocumentMouseUp(event) {
event.preventDefault();
SELECTED = null;
container.style.cursor = 'auto';
}
Because the object position is based off of the plane and the objects are grouped, the problem lies in the INTERSECTED block of the onDocumentMouseMove() event.
This does not get triggered because the raycaster is not recursively checking the objects array. By changing the intersectObjects() function to have a second parameter of true, the problem will be solved.
var intersects = raycaster.intersectObjects(objects, true);
if (intersects.length > 0) {
if (INTERSECTED != intersects[0].object) {
INTERSECTED = intersects[0].object;
plane.position.copy(INTERSECTED.position);
plane.lookAt(camera.position);
updateLightPosition();
}
container.style.cursor = 'pointer';
} else {
INTERSECTED = null;
container.style.cursor = 'auto';
}
Related
You can switch between data visualization in the web application. It first loads the map in this case is that still and image, when you join the page for the first time. As an user you have the option to choose between subject map still an image and the three.js data visualization. But when I run the code of the three.js somehow the <select> with <option> elements are not responding any more. but the <input class="form-check-input" type="checkbox" value="comp_exercise" id=""> still works and is select able. it happened after I used orbit control from the three.js library.
So far I have tried to remove all the event.preventDefault(); but it made no difference. also I have looked in the console if the canvas was overlapping the select element what it was not. the console is not returning any errors and changing the css or position on the web page of the select element does not make a difference either.
the expected is that the <select> still works after the three.js codes get runs so that you can change metric but it is not clickable anymore and left click does not work either
the html code thats not working anymore after the three.js codes get runs:
<div class="form-group">
<label for="exampleFormControlSelect1">Main metric</label>
<select class="form-control" id="exampleFormControlSelect1">
<optgroup label = "Environmental factors">
<option value="air">Air pollution</option>
<option value="noise">Noise pollution</option>
</optgroup>
</select>
</div>
how the three.js code get activated
<div class="float-left"><button class="btn btn-success btn-sm button_toggle_dataviz" onclick="toggle_toDataviz()">Dataviz</button></div>
var displayMap = false;
var display3dData = false;
var displayStory = false;
function toggle_toDataviz() {
document.getElementById("dataviz_charts").style.display = "block";
document.getElementById("dataviz_map").style.display = "none";
document.getElementById("form-container-share-story").style.display =
"none";
displayMap = false;
display3dData = true;
displayStory = false;
start3DdataViz();
}
I am importing these links:
Tweenmax.min.js
orbitcontrol.js
three.js
the three.js full code:
var booleanCentrum = false;
var booleanZuid = false;
var booleanWest = false;
var booleanOost = false;
var booleanNoord = false;
var booleanNieuwWest = false;
var booleanZuidOost = false;
var booleanWestpoort = false;
function start3DdataViz() {
if(display3dData) {
document.getElementById('data_viz_cubes_update').innerHTML = '';
document.getElementById('data_viz_cubes_update').setAttribute("style", "height:500px");
let a = window.getComputedStyle(document.getElementById("data_viz_cubes_update"), null);
var canvasHeight = parseInt(a.getPropertyValue("height").substring(0, a.getPropertyValue("height").length - 2));
var canvasWidth = parseInt(a.getPropertyValue("width").substring(0, a.getPropertyValue("width").length - 2));
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, canvasWidth / canvasHeight, 0.1, 1000);
var controls = new THREE.OrbitControls(camera);
camera.position.z = 20;
controls.update();
$("#data_viz_cubes_update").hover(
function () {
noscroll()
}
);
var noscroll_var;
function noscroll() {
if (noscroll_var) {
document.getElementsByTagName("html")[0].style.overflowY = "";
document.body.style.paddingRight = "0";
controls.enableZoom = false;
controls.enablePan = false;
controls.enableRotate = false;
noscroll_var = false
} else {
document.getElementsByTagName("html")[0].setAttribute('style', 'overflow-y: hidden !important');
document.body.style.paddingRight = "17px";
controls.enableZoom = true;
controls.enablePan = true;
controls.enableRotate = true;
noscroll_var = true
}
}
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor("#e3e0e5");
renderer.setSize(canvasWidth, canvasHeight);
var canvas = document.getElementById('data_viz_cubes_update');
renderer.setSize($(canvas).width(), $(canvas).height());
canvas.appendChild(renderer.domElement);
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshLambertMaterial({color: "red", transparent: true});
for (let i = 0; i < 8; i++) {
let b = "";
0 === i ? b = "Centrum" : 1 === i ? b = "Zuid" : 2 === i ? b = "West" : 3 === i ? b = "Oost" : 4 === i ? b = "Noord" : 5 === i ? b = "NieuwWest" : 6 === i ? b = "ZuidOost" : 7 === i && (b = "Westpoort");
var mesh = new THREE.Mesh(geometry, material);
mesh.material.opacity = 1;
mesh.name = b;
mesh.position.x = (Math.random() - 0.5) * 20;
mesh.position.y = (Math.random() - 0.5) * 20;
mesh.position.z = (Math.random() - 0.5) * 20;
scene.add(mesh);
}
var light = new THREE.PointLight('white', 1, 1000);
light.position.set(0, 0, 0);
scene.add(light);
var render = function () {
requestAnimationFrame(render);
controls.update();
renderer.render(scene, camera);
};
render();
function onMouseMove(event) {
event.preventDefault();
var rect = event.target.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / canvasWidth) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / canvasHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(scene.children, true);
for (var i = 0; i < intersects.length; i++) {
booleanCentrum = false;
booleanZuid = false;
booleanWest = false;
booleanOost = false;
booleanNoord = false;
booleanNieuwWest = false;
booleanZuidOost = false;
booleanWestpoort = false;
switch(intersects[i].object.name){
case "Centrum":
booleanCentrum = true;
break;
case "Zuid":
booleanZuid = true;
break;
case "West":
booleanWest = true;
break;
case "Oost":
booleanOost = true;
break;
case "Noord":
booleanNoord = true;
break;
case "NieuwWest":
booleanNieuwWest = true;
break;
case "ZuidOost":
booleanZuidOost = true;
break;
case "Westpoort":
booleanWestpoort = true;
break;
default:
console.log('not on object');
}
this.tl = new TimelineMax();
this.tl.to(intersects[i].object.scale, 1, {x: 2, ease: Expo.easeOut});
this.tl.to(intersects[i].object.scale, .5, {x: 1, ease: Expo.easeOut});
}
}
window.addEventListener('mousemove', onMouseMove);
}
}
I have a group of clickable objects that can be spun either using the scroll wheel or by clicking and dragging. The problem I am having is when you drag and then release on top of an object, it will launch the click animation.
Here is a link to a fiddle of the simplified project.
Does anyone know how to stop the click from being recorded during the drag? It seems like its being a bit more difficult because they are raycasted objects.
My js functions:
document.addEventListener("mousedown", function () {
isMouseDown = true
startX = event.pageX
startY = event.pageY
document.body.style.cursor = 'grabbing';
})
document.addEventListener("mouseup", function () {
isMouseDown = false
document.body.style.cursor = 'grab';
})
document.addEventListener("mousemove", function (event) {
if (isMouseDown) {
document.body.style.cursor = 'grabbing'
}
aimX = ((window.innerWidth / 2) - event.pageX) * 0.35
aimY = ((window.innerHeight / 2) - event.pageY) * 0.5
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera(mouse, camera)
const intersections = raycaster.intersectObjects(group.children)
if (intersections.length > 0) {
if(projectHov){
if (INTERSECTED != intersections[0].object) {
if (INTERSECTED)
INTERSECTED.material.color.setHex(INTERSECTED.currentHex);
INTERSECTED = intersections[0].object;
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
//setting up new material on hover
INTERSECTED.material.color.setHex( 0xadadad );
if (INTERSECTED){
if(projectHov){
document.body.style.cursor = "pointer"
}
}
}
}
} else {
if (INTERSECTED) INTERSECTED.material.color.setHex(INTERSECTED.currentHex);
document.body.style.cursor = "grab";
INTERSECTED = null;
}
// Moving the objects ==========================
if(isMouseDown) {
let currentRotation = new THREE.Matrix4();
currentRotation.makeRotationFromEuler(group.rotation);
let newEuler = new THREE.Euler(0, ((event.pageX - startX) + (event.pageY - startY)) / 325, 0);
let newRotation = new THREE.Matrix4();
newRotation.makeRotationFromEuler(newEuler);
let finalRotation = new THREE.Matrix4();
finalRotation.multiplyMatrices(newRotation, currentRotation);
group.rotation.setFromRotationMatrix(finalRotation);
startX = event.pageX;
startY = event.pageY;
}
})
section.addEventListener('click', onDocumentMouseDown, false);
function onDocumentMouseDown(event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersections = raycaster.intersectObjects(objects)
// Animation =================================
if (intersections.length > 0){
if(projectHov){
tl.play();
tl.to(backTag, 1, {
delay: 1.25,
autoAlpha:1,
}, 0);
}
// Animation ===================================
if (intersections[0].object == old ) {
if(projectHov){
tlOld.play();
projectHov = false
tlOld.to(old, 1.5, {three:{scaleX: 1.5, scaleY: 1.5, x:0, y:0, z:0}, ease:Power2.easeInOut}, 0);
tlOld.to(fnup, 1, {three:{y:-4000, opacity: 0 }, ease:Power2.easeInOut}, 0);
tlOld.to(alex, 1, {three:{y:-4000, opacity: 0 }, ease:Power2.easeInOut}, 0);
tlOld.to(cam, 1, {three:{y:-4000, opacity: 0 }, ease:Power2.easeInOut}, 0);
tlOld.to(mirrorCube, 1, {three:{y:-400, opacity: 0 }, ease:Power2.easeInOut}, 0);
groupRotate = false
}
}
You may want to store intersections[0].object.uuid; in a variable and update it on mousedown and mouseup. Then compare stored uuid with raycasted object uuid on mouse up. Process further only if they are equal.
I want to set a different color to an object, which I selected in Three JS. I am getting the x and y position of the mouse, but the function raycaster.intersectObjects() always returns an empty array.
I am loading the data here and filling the objects array:
var objects = [];
var mousev,raycaster, scene, renderer, controls, loader;
//Load Data
loader = new THREE.ObjectLoader();
loader.load("models/selectableObjects.json", function (obj) {
scene.add(obj);
scene.traverse(function(children){
objects.push(children);
});
},
function (xhr) {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
},
function (err) {
console.error('An error happened');
}
);
That's my mouse down Event listener:
raycaster = new THREE.Raycaster();
mousev = new THREE.Vector2();
document.addEventListener('mousedown', onDocumentMouseDown, false);
function onDocumentMouseDown(event){
event.preventDefault();
mousev.x = (event.clientX / renderer.domElement.width) * 2 - 1;
console.log("Mouse position X: "+mousev.x);
mousev.y = (event.clientY / renderer.domElement.height) * 2 + 1;
console.log("Mouse position Y: "+mousev.y);
raycaster.setFromCamera( mousev, camera );
var intersects = raycaster.intersectObjects( objects );
console.log("Objects: ");
console.log(objects);
console.log("Intersects: ");
console.log(intersects);
var color = (Math.random() * 0xffffff);
for ( var i = 0; i < intersects.length; i++ ) {
intersects[i].object.material.color.set( 0xff0000 );
}
}
Thank you for your help.
I'm completely new to three.js and 3D. I'm trying to make a really simple first person shooter. I found heaps of examples but they all look really complicated. I want to understand the code before I use it. What I am having trouble with is the camera rotation. Everything else is fine. My approach doesn't quite work. It seems to be rotating on the z axis even though I'm setting that to 0. Here's all my code (125 lines)
var width = window.innerWidth, height = window.innerHeight,
scene = new THREE.Scene(),
camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000),
renderer = new THREE.WebGLRenderer(),
mouse = {
x: 0,
y: 0,
movedThisFrame: false
};
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({
color: 0x00ff00
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
var floorgeometry = new THREE.BoxGeometry(100,0.1,100);
var floormaterial = new THREE.MeshBasicMaterial({
color: 0xff0000
});
var floor = new THREE.Mesh(floorgeometry, floormaterial);
floor.position.y = -1;
scene.add(floor);
var keys = {
w: false,
a: false,
s: false,
d: false
};
camera.position.z = 5;
function radDeg(radians) {
return radians * 180 / Math.PI;
}
function degRad(degrees) {
return degrees * Math.PI / 180;
}
function rotateCam() {
if (!mouse.movedThisFrame) {
mouse.x = 0;
mouse.y = 0;
}
/*
What am I doing wrong here?
*/
camera.rotation.x -= mouse.y * 0.001;
camera.rotation.y -= mouse.x * 0.001;
camera.rotation.z = 0;
mouse.movedThisFrame = false;
}
function moveCam() {
var rotation = camera.rotation.y % (Math.PI * 2), motion = [0,0];
if (keys.w) {
motion[0] += 0.1 * Math.cos(rotation);
motion[1] += 0.1 * Math.sin(rotation);
}
if (keys.a) {
motion[0] += 0.1 * Math.cos(rotation + degRad(90));
motion[1] += 0.1 * Math.sin(rotation + degRad(90));
}
if (keys.s) {
motion[0] += 0.1 * Math.cos(rotation - degRad(180));
motion[1] += 0.1 * Math.sin(rotation - degRad(180));
}
if (keys.d) {
motion[0] += 0.1 * Math.cos(rotation - degRad(90));
motion[1] += 0.1 * Math.sin(rotation - degRad(90));
}
camera.position.z -= motion[0];
camera.position.x -= motion[1];
}
window.onload = function() {
renderer.domElement.onclick = function() {
console.log('requested pointer lock');
renderer.domElement.requestPointerLock();
};
renderer.domElement.onmousemove = function(e) {
if (!mouse.movedThisFrame) {
mouse.x = e.movementX;
mouse.y = e.movementY;
mouse.movedThisFrame = true;
}
};
document.onkeydown = function(e) {
var char = String.fromCharCode(e.keyCode);
if (char == 'W')
keys.w = true;
else if (char == 'A')
keys.a = true;
else if (char == 'S')
keys.s = true;
else if (char == 'D')
keys.d = true;
};
document.onkeyup = function(e) {
var char = String.fromCharCode(e.keyCode);
if (char == 'W')
keys.w = false;
else if (char == 'A')
keys.a = false;
else if (char == 'S')
keys.s = false;
else if (char == 'D')
keys.d = false;
};
function animate() {
requestAnimationFrame(animate);
rotateCam();
moveCam();
renderer.render(scene, camera);
}
animate();
};
The problem is in the rotateCam function. It doesn't quite work and I don't really know why.
I also tried using the code on this question but it didn't work.
First person controls are more complicated than you may think. Even if you figure out your angle math, when the pointer is not locked, the mouse hits the window edge and turning stops.
I suggest you start with the pointer lock example (http://threejs.org/examples/#misc_controls_pointerlock) which is an example of first person controls for 3js.
When I drag the mouse left or right, i'd like drag the scene, don't rotate camera.
I tried
camera.position.x = mouseX;
camera.position.y = mouseY;
but scene rotated
I tried change position in scene — scene rotated.
How to drag the scene?
You can try using (after you define your camera)
controls = new THREE.RollControls(camera);
controls.movementSpeed = 10;
controls.lookSpeed = 1;
controls.rollSpeed = 0;
controls.autoForward = false;
after including this in your html:
<script type="text/javascript" src="three.js/examples/js/controls/RollControls.js"></script>
In addition you would have to change your onWindowResize event to add
controls.handleResize();
and your render() function to add
controls.update(clock.getDelta());
and your init() function to add
clock = new THREE.Clock();
here is a file i got at github that might work
THREE.DragControls = function(_camera, _objects, _domElement) {
if (_objects instanceof THREE.Scene) {
_objects = _objects.children;
}
var _projector = new THREE.Projector();
var _mouse = new THREE.Vector3(),
_offset = new THREE.Vector3();
var _selected;
_domElement.addEventListener('mousemove', onDocumentMouseMove, false);
_domElement.addEventListener('mousedown', onDocumentMouseDown, false);
_domElement.addEventListener('mouseup', onDocumentMouseUp, false);
function onDocumentMouseMove(event) {
event.preventDefault();
_mouse.x = (event.clientX / _domElement.width) * 2 - 1;
_mouse.y = -(event.clientY / _domElement.height) * 2 + 1;
var ray = _projector.pickingRay(_mouse, _camera);
if (_selected) {
var targetPos = ray.direction.clone().multiplyScalar(_selected.distance).addSelf(ray.origin);
_selected.object.position.copy(targetPos.subSelf(_offset));
return;
}
var intersects = ray.intersectObjects(_objects);
if (intersects.length > 0) {
_domElement.style.cursor = 'pointer';
} else {
_domElement.style.cursor = 'auto';
}
}
function onDocumentMouseDown(event) {
event.preventDefault();
_mouse.x = (event.clientX / _domElement.width) * 2 - 1;
_mouse.y = -(event.clientY / _domElement.height) * 2 + 1;
var ray = _projector.pickingRay(_mouse, _camera);
var intersects = ray.intersectObjects(_objects);
if (intersects.length > 0) {
_selected = intersects[0];
_offset.copy(_selected.point).subSelf(_selected.object.position);
_domElement.style.cursor = 'move';
}
}
function onDocumentMouseUp(event) {
event.preventDefault();
if (_selected) {
_selected = null;
}
_domElement.style.cursor = 'auto';
}
}