what I'm trying to achieve: at first, you're only able to select objects within the group "interactable". when you click on an "interactable" object, you can now select any object within group "master". (you're picking up an interactable then selecting an object to place it on.)
"master" contains the group "interactable". right now I'm only able to switch from selecting within "interactable" to selecting within "master" without "interactable".
here's my code:
function render() {
raycaster.setFromCamera(mouse, camera);
if (obj_selected) {intersects = raycaster.intersectObjects(master.children)}
else {intersects = raycaster.intersectObjects(interactable.children)}
if (intersects.length > 0) {
if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
INTERSECTED = intersects[0].object;
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex( 0xf4425f );
} else {
if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
INTERSECTED = null;
}
renderer.render(scene, camera);
}
I hope my explanation wasn't too confusing. thanks in advance.
never mind, I figured it out!
so instead of putting things in groups, I added meshes to an array. I have three arrays: master, static, and interactable. if I get the intersections for master, then I can access all the objects in my scene without messing with groups.
conclusion: I don't think I should use groups when I'm simply categorizing things.
Related
I have several objects grouped in a Object3D. I wanna calculate the boundingbox of the whole group, except some specific objects in that group.
Can you disable the calculation of boundingbox for those objects?
Can you disable the calculation of boundingbox for those objects?
If you are using Box3.setFromObject() then no, this is not possible. The code processes all children of the hierarchy and expands the AABB if an object has a geometry property.
three.js R101
As Mugen mentioned it's not possible to do this out of the box but you can achieve it by manually traversing the tree.
Here's an idea for how you might do that.
var box = null;
group.traverse(c => {
// logic for whether or not to include the child
var includeChild = c.isMesh;
if (includeChild) {
// initialize the box to the first valid child found
// otherwise expand the bounds
if (box === null) {
box = new THREE.Box3();
box.setFromObject(c);
} else {
box.expandByObject(c);
}
}
});
You can change the boolean logic for includeChild to determine whether or not you want an object to be included in the bounds calculations or not.
Hope that helps!
I have two different threejs scenes and each has its own camera. I can control each camera individually with a corresponding TrackballControls instance.
Is there a reliable way to 'lock' or 'bind' these controls together, so that manipulating one causes the same camera repositioning in the other? My current approach is to add change listeners to the controls and update both cameras to either's change, but this isn't very neat as, for one, both controls can be changing at once (due to dampening).
I believe it should work if you set the matrices of the second camera to the values of the first and disable automatic matrix-updates of both cameras:
camera2.matrix = camera1.matrix;
camera2.projectionMatrix = camera1.projectionMatrix;
camera1.matrixAutoUpdate = false;
camera2.matrixAutoUpdate = false;
But now you need to update the matrix manually in your renderloop:
camera1.updateMatrix();
That call will take the values for position, rotation and scale (that have been updated by the controls) and compose them into camera1.matrix, which per assignment before is also used as the matrix for the second camera.
However, this feels a bit hacky and can lead to all sorts of weird problems. I personally would probably prefer the more explicit approach you have already implemented.
Question is why are you even using two camera- and controls-instances? As long as the camera isn't added to the scene you can just render both scenes using the same camera.
Is it possible to use the Observer or Publisher design patterns to control these objects?
It seems that you are manipulating the cameras with a control. You might create an object that has the same control interface, but when you pass a command to the object, it repeats that same command to each of the subscribed or registered cameras.
/* psuedo code : es6 */
class MasterControl {
constructor(){
this.camera_bindings = [];
}
control_action1(){
for( var camera of this.camera_bindings ){
camera.control_action1();
}
}
control_action2( arg1, arg2 ){
for( var camera of this.camera_bindings ){
camera.control_action2( arg1, arg2 );
}
}
bindCamera( camera ){
if( this.camera_bindings.indexOf( camera ) === -1 ){
this.camera_bindings.push( camera );
}
}
}
var master = new MasterControl();
master.bindCamera( camera1 );
master.bindCamera( camera2 );
master.bindCamera( camera3 );
let STEP_X = -5;
let STEP_Y = 10;
//the following command will send the command to all three cameras
master.control_action2( STEP_X, STEP_Y );
This binding is self created rather than using native three.js features, but it is easy to implement and can get you functional quickly.
Note: I wrote my psuedocode in es6, because it is simpler and easy to communicate. You can write it in es5 or older, but you must change the class definition into a series of functional object definitions that create the master object and its functionality.
From A-Frame, you can access an entity's object3D with .object3D or .getObject3D(), is there a way to do the reverse, when you are working with three.js objects, to get the element an object belongs to? Perhaps adding the parent element to userdata?
A-Frame attaches the entity to object3D as .el.
For example with an entity with a mesh:
document.querySelector('a-entity').getObject3D('mesh').el;
It also attaches to the group object3D:
document.querySelector('a-entity').object3D.el;
This is done during setObject3D().
For object3Ds that A-Frame does not directly manage and does not have a direct associated A-Frame entity, then we can walk up the scene graph to find the closest one with object3D.traverseAncestors.
I wrote a small function that traverses upwards using traverseAncenstors and returns an element if there is one.
function getParentEl( o ){
let p;
o.traverseAncestors( function( a ){
if( p === undefined && a.el ){
p = a;
}
});
if( p ){
return p.el
}
return;
}
I think the problem might be because it's a loaded object and not a "created" geometry, but I want to be able to select an object and show that it is selected, like in this example.
I load multiple objects in and I keep track of an index for each object (so the first object loaded has objIndex = 0, etc.) and I know that my code recognizes when I "mouse down" from the console. However, it says that intersects.length = 0 so the rest of the function is skipped.
I'm also not sure what "intersects.length" is actually taking the length of.
So I suppose my questions are:
What is "intersects.length" taking the length of?
Why do my objects have an "intersects.length" of 0?
What are some things I could do so that the objects are recognized?
Here's the relevant code:
function onDocumentMouseDown(event) {
event.preventDefault();
// default action of event will not be triggered
var vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(camera);
// used to pass 3D positions and directions
var raycaster = new THREE.Raycaster(camera.position,
vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(objects);
console.log('Mouse is down.');
console.log(intersects.length);
if (intersects.length > 0) {
controls.enabled = false;
SELECTED = intersects[0].object;
var intersects = raycaster.intersectObjects(plane);
offset.copy(intersects[0].point).sub(plane.position);
container.style.cursor = 'move';
console.log('Clicked object: ' + object.name);
}
}
If you need to see more, let me know! Thank you. :)
Answering your question:
Here is an interesting read on raycast to help clarify raycasting:
http://soledadpenades.com/articles/three-js-tutorials/object-picking/
So what raycasting basically do is to (really) cast an imaginary ray. When you "mouse down" your program will, so called, cast a straight-line array into the scene. Now to answer your question:
For question one(1) and two(2) : Now when you cast a ray, it may or may not intersect an object. For example if you click into an empty space, the ray will not catch an object. However, as the name suggest, a raycast cast a ray and it may also hits multiple objects along the way. For example if you have one object and another object positioned directly behind the first object, casting a ray into the first object will also intersects the second object. The ray cast will "catch" both objects and put in the two objects into into the array "intersects". Therefore, the command
if (intersects.length > 0)
stated that if the raycast "catches" and object, do these. Moreover, it keeps on calling intersects[0] to refer to the first objects in the intersects array. In this case the most front object that the raycast catches. You can test my answer by creating hundreds of objects and state that, for every member in the intersects array, you change the color to red(for example). You will see that if the raycast catches multiple objects, all the objects will turn into that color. Hope for the first two question!
I am not sure of what you are asking for the third question. Can you clarify further? One way for the object to be recognized is to put them into the scene and put a raycast on the scene. However, I am not too sure of what you are asking to give you answer that I think you want.
I have put some grids into my scene with the gridhelper. However, it doesn't always remove itself from the scene when I delete it.
Here is the code where I go through a delete the grids and the axis. Why doesn't this remove the grids from the scene? Sometimes it works and sometimes it doesn't.
for (var ObjID in this.Scene.__objects)
{ if (this.Scene.__objects[ObjID] instanceof THREE.GridHelper){ this.Scene.remove(this.Scene.__objects[ObjID]); this.Scene.__objects.splice(ObjID,1); }
if (this.Scene.__objects[ObjID] instanceof THREE.AxisHelper){ this.Scene.remove(this.Scene.__objects[ObjID]); this.Scene.__objects.splice(ObjID,1); }
}
Give the GridHelper a name when you create it:
var grid = new THREE.GridHelper(1000, 5);
grid.position.set(45,0,25);
grid.name = "GridHelper";
scene.add(grid);
Then you can call:
scene.remove(scene.getObjectByName("Grid"));
Or if you change your grid Object Variable to a global variable you can just use:
scene.remove(grid);
Your Code is wrong and don't works everytime because the Grid-Center (obj.geometry.BoundingSphere.center.x / y / z) is'nt always in the scene, so the If-query won't work right all time.