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.
Related
is there any way to know if there is an object in the scene? is there any function like isEmpty() or something ?
The THREE.Scene is the root of a scene graph, and if scene.children.length > 1 then it has at least one child / object in that scene. Whether any children are things that can be renderered depends on their type. Lights and Cameras affect the scene but are not rendered themselves. The easiest way to check for objects that could be rendered is to traverse the scene and search for Mesh instances:
let hasMesh = false;
scene.traverse(function (object) {
if (object.isMesh) hasMesh = true;
});
console.log(hasMesh ? 'Found meshes!' : 'No meshes.');
Depending on the use case you may want to check for other types, or to check properties like object.visible if you are showing/hiding objects dynamically.
I'm fairly new to three.js and trying to get a better understanding of ray casting. I have used it so far in a game to show when an object collides with another on the page which works perfectly. In the game I'm building this is to take health from the hero as it crashes into walls.
I am now trying to implement a target which when hovered over some objects it will auto shoot. However the target only registers a target hit once the object passes through the target mesh rather than when its ray is cast through it.
to further detail this, the ray is cast from the camera through the target, if the target is on a mesh (stored as an object array) then I want it to trigger a function.
within my update function I have this:
var ray = new THREE.Raycaster();
var crossHairClone = crossHair.position.clone();
var coards = {};
coards.x = crossHairClone.x
coards.y = crossHairClone.y
ray.setFromCamera(coards, camera);
var collisionResults = ray.intersectObjects( collidableMeshList );
if ( collisionResults.length > 0 ) {
console.log('Target Hit!', collisionResults)
}
The console log is only triggered when the collidableMeshList actually touches the target mesh rather than when it is aiming at it.
How do I extend the ray to pass through the target (which I thought it was already doing) so that if anything hits the ray then it triggers my console log.
Edit
I've added a URL to the game in progress. There are other wider issues with the game, my current focus is just the target ray casting.
Game Link
Many thanks to everyone who helped me along the way on this one but I finally solved it, only not through the means that I had expected to. Upon reading into the setFromCamera() function it looks like it is mainly used around mouse coards which in my instance was not what I wanted. I wanted a target on the page and the ray to pass through this. For some reason my Ray was shooting vertically up from the centre of the whole scene.
In the end I tried a different approach and set the Rays position and direction directly rather rely on setting it from the cameras position.
var vector = new THREE.Vector3(crossHair.position.x, crossHair.position.y, crossHair.position.z);
var targetRay = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var enemyHit = targetRay.intersectObject( enemy );
if ( enemyHit.length > 0 ) {
console.log('Target Hit!', targetRay)
}
I'll leave this up for a while before accepting this answer in case anyone has a better approach than this or has any corrections over what I have said in regards to setFromCamera().
I have a 3D scene with a bunch of CSS object that I want to rotate so that they are all pointing towards a point in the space.
My CSS objects are simple rectangles that are a lot wider than they are high:
var element = document.createElement('div');
element.innerHTML = "test";
element.style.width = "75px";
element.style.height = "10px";
var object = new THREE.CSS3DObject(element);
object.position.x = x;
object.position.y = y;
object.position.z = z;
Per default, the created objects are defined as if they are "facing" the z-axis. This means that if I use the lookAt() function, the objects will rotate so that the "test" text face the point.
My problem is that I would rather rotate so that the "right edge" of the div is pointing towards the desired point. I've tried fiddling with the up-vector, but I feel like that wont work because I still want the up-vector to point up. I also tried rotating the object Math.PI/2 along the y axis first, but lookAt() seems to ignore any prior set rotation.
It seems like I need to redefine the objects local z-vector instead, so that it runs along with the global x-vector. That way the objects "looking at"-direction would be to the right in the scene, and then lookAt() would orient it properly.
Sorry for probably mangling terminology, newbie 3D programmer here.
Object.lookAt( point ) will orient the object so that the object's internal positive z-axis points in the direction of the desired point.
If you want the object's internal positive x-axis to point in the direction of the desired point, you can use this pattern:
object.lookAt( point );
object.rotateY( - Math.PI / 2 );
three.js r.84
I am using chrome experiment's globe http://globe.chromeexperiments.com and I would like to click on any color coordinates to show popup but event listener is not being called. can somebody assist me how to do it?
You could try casting a ray from the camera into the scenery and then getting the intercepts. See the docs for rayCaster.
What you would do is create a new rayCaster object, then get the mouse coordinates of the user's screen. You would need to create some kind of "onclick" event on the parent element of the canvas.
Once the event occurs, the rayCaster object has a property which handles our situation, "setFromCamera". However, you need to provide it a list of all of the objects that you are interested in intersecting, that is all of the rectangles.
So you code would look something like this, note that I used jQuery for this:
var rayCaster = new THREE.Raycaster();
var onClickEventHandler = function(e) {
//Save the mouse data as a vector
//width and height pertain to the size of the canvas.
var mousePosition = new THREE.Vector3((e.offsetX / width) * 2 - 1,
-(e.offsetY / height) * 2 + 1,
0.5);
rayCaster.setFromCamera(mousePosition, camera);
//"objects" is the array of meshes that you care about the user intersecting
var intersects = rayCaster.intersectObjects(objects);
}
What is contained inside the "intersects" array is all of the items from the "objects" array which ere intersected by the ray in the order of intersection. You are likely interested in the first element of this array.
I don't know if that globe utility provides a better way of doing it, what I showed is the generic way of selecting an object in the scenery.
I've just started learning THREE and have been messing about with the three.js example of controllable MD2 characters to try and fashion it into a 3rd person shooter kind of game. I've been trying to write a simple algorithm for the enemy characters and I'm pretty sure that ray-casting would be ideal.The whole idea is that the enemies should stop rotating once they're facing the player. But Here's the problem that's giving me sleepless nights! :
Let's say, the enemy object is the origin for the ray caster ray. No matter what direction I set for the direction of that ray ( even, for example (1,0,0) - the positive x-axis), the ray's direction is always pointing towards the center of the scene!!!
Please help! haven't been able to find any Example online for this kind of use for the ray caster (apart from collision detection which I really don't need at the moment).
If all you want is for enemies to stop rotating when they are looking at the player, I would consider just checking the direction between them, as it's a lot faster than casting a ray to see if it intersects:
// Assuming `enemy` is a THREE.Mesh
var targetDir = enemy.position.clone().sub(player.position).normalize();
var currentDir = (new THREE.Vector3()).applyMatrix4(enemy.matrixWorld).sub(enemy.position).normalize();
var amountToRotate = currentDir.sub(targetDir);
var offset = amountToRotate.length();
Then rotate each axis no more than the value for that axis in amountToRotate if offset is greater than some threshold.
That said, here is how you use a Raycaster, given the variables above:
var raycaster = new THREE.Raycaster(enemy.position, targetDir);
var intersections = raycaster.intersectObject(player);
Note that if you are running any of the above code in an animation loop, it will create a lot of garbage collection churn because you are constantly creating a bunch of new objects and then immediately throwing them away. A better pattern, which is used a lot in the library itself, is to initialize objects once, copy values to them if you need to, and then use those copies for computation. For example, you could create a function to do your raycasting for you like this:
var isEnemyLookingAtPlayer = (function() {
var raycaster = new THREE.Raycaster();
var pos = new THREE.Vector3();
return function(enemy) {
raycaster.ray.origin.copy(enemy.position);
raycaster.ray.direction.copy(pos.copy(enemy.position).sub(player.position).normalize());
return !!raycaster.intersectObject(player).length;
};
})();