-
-
Notifications
You must be signed in to change notification settings - Fork 36k
Add sprite.raycast support for scale,rotation,center #14292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@WestLangley Any thoughts about this one? |
If you set the scale to be ( 1, 10, 1 ), it does not work well because the sprite is approximated as a sphere. If we are going to the trouble to support sprite transforms, we should probably make click events on sprites accurate. A sprite is just two billboarded triangles. I expect we could use |
@WestLangley Hi, I updated the code to use |
src/objects/Sprite.js
Outdated
function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { | ||
|
||
// compute position in camera space | ||
alignedPosition.set( ( vertexPosition.x - center.x ) * scale.x, ( vertexPosition.y - center.y ) * scale.y ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You omitted the 0.5 offset (on purpose, I assume) because you compensated for it elsewhere. I am not a fan of doing that because this formula now looks incorrect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In theory it should be::
alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale );
// compute position in camera space | ||
alignedPosition.set( ( vertexPosition.x - center.x ) * scale.x, ( vertexPosition.y - center.y ) * scale.y ); | ||
|
||
if ( sin !== undefined ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not a fan of this either, to tell you the truth. You are really checking if rotation
is zero.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to cache the sin and cos value.I will add a comment here
src/objects/Sprite.js
Outdated
|
||
var center = this.center; | ||
|
||
transformVertex( vA.set( 0, 0, 1 ), mvPosition, center, worldScale, sin, cos ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is where you compensate for the missing offset. If you are not going to change this, at least add add a comment here... Why is the z-coordinate not zero?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because the z is not used, i can change it to zero.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will refact it to show the calculated offset
Nice work! And thank you for the excellent example! Make sure you test the case where the sprite parent is transformed. Unrelated tip: You don't need to instantiate a new |
@WestLangley I updated the code and example, added the case where the sprite parent is transformed. |
Your triangles have the incorrect winding order, and consequently, the front face is incorrect. This is why you have to prevent back-face culling to get the correct result. The face vertices should be counterclockwise. Back-face culling should be Specify the faces in the same order as they are specified in var faces = new Uint16Array( [
0, 1, 2,
0, 2, 3
] ); Like so: transformVertex( vA.set( - 0.5, - 0.5, 0 ), mvPosition, center, worldScale, sin, cos );
transformVertex( vB.set( 0.5, - 0.5, 0 ), mvPosition, center, worldScale, sin, cos );
transformVertex( vC.set( 0.5, 0.5, 0 ), mvPosition, center, worldScale, sin, cos );
// check first triangle
var intersect = raycaster.ray.intersectTriangle( vA, vB, vC, true, intersectPoint ); |
In the |
You are correct. Since Even so, one of your triangles faces away from the camera; the other one towards. As I suggested, I would correct that as a courtesy to future maintainers. Specify the faces in the same order as they are specified in |
I updated the code to specify the faces in the same order as they are specified in |
@mrdoob Can this be merged to the dev? |
Thanks! |
Thanks! Is there a demo of this on the threejs.org docs? If not, may I suggest we add something like the rawgit.com demo to the docs? |
@06wj thanks! |
could anyone of you also copy this bit? since it does not return triangle info, one has to re-implement the entire raycast to get uvs now |
here is altered version: THREE.Sprite.prototype.raycast = ( function () {
var intersectPoint = new THREE.Vector3();
var worldScale = new THREE.Vector3();
var mvPosition = new THREE.Vector3();
var alignedPosition = new THREE.Vector2();
var rotatedPosition = new THREE.Vector2();
var viewWorldMatrix = new THREE.Matrix4();
var vA = new THREE.Vector3();
var vB = new THREE.Vector3();
var vC = new THREE.Vector3();
function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) {
// compute position in camera space
alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale );
// to check if rotation is not zero
if ( sin !== undefined ) {
rotatedPosition.x = ( cos * alignedPosition.x ) - ( sin * alignedPosition.y );
rotatedPosition.y = ( sin * alignedPosition.x ) + ( cos * alignedPosition.y );
} else {
rotatedPosition.copy( alignedPosition );
}
vertexPosition.copy( mvPosition );
vertexPosition.x += rotatedPosition.x;
vertexPosition.y += rotatedPosition.y;
// transform to world space
vertexPosition.applyMatrix4( viewWorldMatrix );
}
var barycoord = new THREE.Vector3();
function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) {
THREE.Triangle.getBarycoord( point, p1, p2, p3, barycoord );
uv1.multiplyScalar( barycoord.x );
uv2.multiplyScalar( barycoord.y );
uv3.multiplyScalar( barycoord.z );
uv1.add( uv2 ).add( uv3 );
return uv1.clone();
}
return function raycast( raycaster, intersects ) {
worldScale.setFromMatrixScale( this.matrixWorld );
viewWorldMatrix.getInverse( this.modelViewMatrix ).premultiply( this.matrixWorld );
mvPosition.setFromMatrixPosition( this.modelViewMatrix );
var rotation = this.material.rotation;
var sin, cos;
if ( rotation !== 0 ) {
cos = Math.cos( rotation );
sin = Math.sin( rotation );
}
var center = this.center;
transformVertex( vA.set( - 0.5, - 0.5, 0 ), mvPosition, center, worldScale, sin, cos );
transformVertex( vB.set( 0.5, - 0.5, 0 ), mvPosition, center, worldScale, sin, cos );
transformVertex( vC.set( 0.5, 0.5, 0 ), mvPosition, center, worldScale, sin, cos );
var uvA = new THREE.Vector2( 0, 0 );
var uvB = new THREE.Vector2( 1, 0 );
var uvC = new THREE.Vector2( 1, 1 );
// check first triangle
var intersect = raycaster.ray.intersectTriangle( vA, vB, vC, false, intersectPoint );
if ( intersect === null ) {
// check second triangle
uvB.set( 0, 1 );
transformVertex( vB.set( - 0.5, 0.5, 0 ), mvPosition, center, worldScale, sin, cos );
intersect = raycaster.ray.intersectTriangle( vA, vC, vB, false, intersectPoint );
if ( intersect === null ) {
return;
}
}
var distance = raycaster.ray.origin.distanceTo( intersectPoint );
if ( distance < raycaster.near || distance > raycaster.far ) return;
intersects.push( {
distance: distance,
point: intersectPoint.clone(),
uv: uvIntersection( intersectPoint, vA, vB, vC, uvA, uvB, uvC ),
face: null,
object: this
} );
};
}() ); |
@makc I'll make a PR for it. |
When I set SpriteMaterial.sizeAttenuation to false, intersectObjects() does not work anymore. Can anyone tell me what to do with this problem? thanks~ |
|
thanks, my library version is 99dev, it's too new.
|
Fixed #13751
eg: https://rawgit.com/06wj/three.js/patch1_build/examples/webgl_raycast_sprite.html