mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.41k stars 35.35k forks source link

Intersection with ParticleSystem - Update? #911

Closed CheapBastard closed 12 years ago

CheapBastard commented 12 years ago

I gather from various readings and my own experimentation that there is no way to do ray intersection with particles in a WebGL particlesystem - is that still correct? I can get the ray intersect to work perfectly with canvas particles, but not with the particlesystem that WebGL requires.

I have read this issue (#351): https://github.com/mrdoob/three.js/issues/351 And that seems like a logical approach, but the THREE.Collisions and CollisionSphere has been removed from the code base.

Is there an updated way to implement this approach? Has CollisionSphere been moved or removed? Do I need to add an actual 'real' sphere to my scene for each particle? If so, why don't I just use spheres instead of particles in the first place - won't that kill the performance?

Please tell me there is a way to handle collisions for a particlesystem! For what it's worth, I am trying to do mouseover/mouseclick events on particles in a system of about 5000 particles.

renegademaster88 commented 12 years ago

You should give each particle a collision object (a regular sphereGeometry or cubeGeometry) that is invisible (.visible = false) then use Ray to intersect to test weather you hit or not. You could approximate each particle as a spherical equation which would speed things up:

http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter1.htm

With 5000 particles you would need some way to divide them spatially as 5000 ray intersects is going to be slow. I think some sort of Binary Space Partitioning Tree would do nicely. Will be tougher if you particles are moving too.

Quite complex code but will be impressive if you can crack it!

CheapBastard commented 12 years ago

Thank you for your reply. I have implemented this and it works almost perfectly. I am getting an intersection on the spheres, but three.js is throwing an exception. Here is how I am adding the spheres to the scene (forget about the particles for now as it is mainly the spheres we care about:

for (var i = 0; i < result.length; i++) {
 vector = new THREE.Vector3(result[i].X, result[i].Y, result[i].Z);
 collisionSphere = new THREE.Mesh(new THREE.SphereGeometry(.1, 6, 6), new THREE.MeshBasicMaterial({color:0xffffff});
 collisionSphere.position = vector;
 collisionSphere.visible = true; //could be false, but I made them visible for troubleshooting
 scene.add(collisionSphere);
}

Now, my hit detection code when the mouse is clicked or moved:

 var x = ((event.clientX + 2) / window.innerWidth) * 2 - 1;
 var y = -((event.clientY + 2) / window.innerHeight) * 2 + 1;
 var vector = new THREE.Vector3(x, y, 0);
 projector.unprojectVector(vector, camera);
 var ray = new THREE.Ray(camera.position, vector.subSelf(camera.position).normalize());

 var intersects = ray.intersectScene(scene);  //ERROR
 if (intersects.length > 0) {
  alert('here');
 }

The error is thrown on the ray.intersectScene call. The error only occurs when the mouse is over a sphere. The error, according to Chrome is: "Uncaught TypeError: cannot call method 'copy' of null" The call stack is intersectScene (line 18) -> intersectObjects (line 19) -> intersectObject (line 21)

Of course, since three.js is minified, it's a bit tough to track down, but here is line 21 of three.js according to Chrome:

c.copy(this.direction),u=m.matrixWorld,i=u.multiplyVector3(i.copy(n.centroid)).subSelf(a),q=i.dot(c),!(q<=0)&&(g=u.multiplyVector3(g.copy(w[n.a].position)),f=u.multiplyVector3(f.copy(w[n.b].position)),e=u.multiplyVector3(e.copy(w[n.c].position)),h=n instanceof THREE.Face4?u.multiplyVector3(h.copy(w[n.d].position)):null,l=m.matrixRotationWorld.multiplyVector3(l.copy(n.normal)),q=c.dot(l),m.doubleSided||(m.flipSided?q>0:q<0)))if(q=l.dot(i.sub(g,a))/q,k.add(a,c.multiplyScalar(q)),n instanceof THREE.Face3)d(k,

Any ideas? It looks like it is trying to copy some vectors for math. One of those must be null, but I have no idea why.

Thanks! BTW, 1,000 spheres is fast enough that I am not sure I even need to use particles at all :)

CheapBastard commented 12 years ago

One more question: when I do show the particles and the spheres together, they don't scale the same way with distance from the camera. The close objects have spheres that are much larger than their particles and the further ones have spheres that are much smaller. I am using a bitmap to render the particles.

This would make for inconsistent behavior when trying to click on a particle - sometimes the invisible bounding sphere is larger and sometimes smaller than the particle I am trying to click on.

Is there some way to adjust to what degree items are scaled with distance from the camera? Thanks again!

renegademaster88 commented 12 years ago

i think you should check your transforms are the same. I have used this technique with 1 particle attached to 1 sphere and it works fine.

CheapBastard commented 12 years ago

Thanks. I haven't done anything special with any transforms - I'm just using whatever the defaults are, but I'll look into it. Any idea on the error during the ray intersection though?

renegademaster88 commented 12 years ago

looks like ray.direction is null.

when i do it i create a new THREE.Ray() then set ray.origin.copy( object.position); ray.direction.copy(object.someVector);

i think you might have an error with the vector you are passing to the 2nd parameter, check the variable in the debugger to make sure its not null.

CheapBastard commented 12 years ago

Nope, origin and direction are both valid. I think I have traced the error to this line in the unminified version of Ray.js: d = face instanceof THREE.Face4 ? objMatrix.multiplyVector3( d.copy( vertices[ face.d ].position ) ) : null;

Here, d is null but face is an instance of Face4, so the d.copy fails. I am not sure why d is null or what d represents though. I see d is initialized to an empty vector - but presumably it was set to null from a previous iteration through this loop where face was not an instance of Face4?

If I have a breakpoint there and enter a console command to force "d" (or "h" as it is in the minified version) = to a new vector, I don't get the error.

CheapBastard commented 12 years ago

I edited my local copy of Ray.js so that line reads:

d = face instanceof THREE.Face4 ? objMatrix.multiplyVector3( d.copy( vertices[ face.d ].position ) ) : *new THREE.Vector3(0,0,0)*;

and it works - I get the intersection back with no error. But I have no idea what other consequence that might have :)

mrdoob commented 12 years ago

What version are you using? The current stable version has that issue fixed already: https://github.com/mrdoob/three.js/blob/master/src/core/Ray.js#L117

CheapBastard commented 12 years ago

I was using r46 -- looks like I missed the bugfix release after that. Thanks!