package rajawali.visitors;
import rajawali.BaseObject3D;
import rajawali.bounds.BoundingBox;
import rajawali.bounds.BoundingSphere;
import rajawali.math.Number3D;
import rajawali.math.Number3D.Axis;
import rajawali.util.Intersector;
public class RayPickingVisitor implements INodeVisitor {
private Number3D mRayStart;
private Number3D mRayEnd;
private Number3D mHitPoint;
private BaseObject3D mPickedObject;
public RayPickingVisitor(Number3D rayStart, Number3D rayEnd) {
mRayStart = rayStart;
mRayEnd = rayEnd;
mHitPoint = new Number3D();
}
public void apply(INode node) {
if(node instanceof BaseObject3D) {
BaseObject3D o = (BaseObject3D)node;
if(!o.isVisible() || !o.isInFrustum()) return;
//RajLog.d("VISITING " + o.getName());
if (o.getGeometry().hasBoundingSphere()) {
BoundingSphere bsphere = o.getGeometry().getBoundingSphere();
bsphere.calculateBounds(o.getGeometry());
bsphere.transform(o.getModelMatrix());
if(intersectsWith(bsphere)) {
if(mPickedObject == null ||
(mPickedObject != null && o.getPosition().z < mPickedObject.getPosition().z))
mPickedObject = o;
}
} else {
// Assume bounding box if no bounding sphere found.
BoundingBox bbox = o.getGeometry().getBoundingBox();
bbox.calculateBounds(o.getGeometry());
bbox.transform(o.getModelMatrix());
if(intersectsWith(bbox)) {
if(mPickedObject == null ||
(mPickedObject != null && o.getPosition().z < mPickedObject.getPosition().z))
mPickedObject = o;
}
}
}
}
private boolean intersectsWith(BoundingBox bbox) {
Number3D raySta = mRayStart;
Number3D rayEnd = mRayEnd;
Number3D boxMin = bbox.getTransformedMin();
Number3D boxMax = bbox.getTransformedMax();
if (rayEnd.x < boxMin.x && raySta.x < boxMin.x) return false;
if (rayEnd.x > boxMax.x && raySta.x > boxMax.x) return false;
if (rayEnd.y < boxMin.y && raySta.y < boxMin.y) return false;
if (rayEnd.y > boxMax.y && raySta.y > boxMax.y) return false;
if (rayEnd.z < boxMin.z && raySta.z < boxMin.z) return false;
if (rayEnd.z > boxMax.z && raySta.z > boxMax.z) return false;
if (raySta.x > boxMin.x && raySta.x < boxMax.x &&
raySta.y > boxMin.y && raySta.y < boxMax.y &&
raySta.z > boxMin.z && raySta.z < boxMax.z)
{mHitPoint.setAllFrom(raySta);
return true;}
if ( (getIntersection(raySta.x-boxMin.x, rayEnd.x-boxMin.x, raySta, rayEnd) && isInBox(boxMin, boxMax, Axis.X))
|| (getIntersection(raySta.y-boxMin.y, rayEnd.y-boxMin.y, raySta, rayEnd) && isInBox(boxMin, boxMax, Axis.Y))
|| (getIntersection(raySta.z-boxMin.z, rayEnd.z-boxMin.z, raySta, rayEnd) && isInBox(boxMin, boxMax, Axis.Z))
|| (getIntersection(raySta.x-boxMax.x, rayEnd.x-boxMax.x, raySta, rayEnd) && isInBox(boxMin, boxMax, Axis.X))
|| (getIntersection(raySta.y-boxMax.y, rayEnd.y-boxMax.y, raySta, rayEnd) && isInBox(boxMin, boxMax, Axis.Y))
|| (getIntersection(raySta.z-boxMax.z, rayEnd.z-boxMax.z, raySta, rayEnd) && isInBox(boxMin, boxMax, Axis.Z)))
return true;
return false;
}
private boolean intersectsWith(BoundingSphere bsphere) {
return Intersector.intersectRaySphere(mRayStart, mRayEnd, bsphere.getPosition(), bsphere.getRadius(), mHitPoint);
}
private boolean getIntersection( float fDst1, float fDst2, Number3D P1, Number3D P2) {
if ((fDst1 * fDst2) >= 0.0f) return false;
if (floatEqual(fDst1, fDst2)) return false;
mHitPoint.setAllFrom(P1);
mHitPoint.add(Number3D.subtract(P2, P1));
mHitPoint.multiply(-fDst1/(fDst2-fDst1));
return true;
}
private boolean floatEqual(float lhs, float rhs) {
return (float)Math.abs(lhs - rhs) < .00001f;
}
private boolean isInBox(Number3D boxMin, Number3D boxMax, Axis axis) {
if ( axis==Axis.X && mHitPoint.z > boxMin.z && mHitPoint.z < boxMax.z && mHitPoint.y > boxMin.y && mHitPoint.y < boxMax.y) return true;
if ( axis==Axis.Y && mHitPoint.z > boxMin.z && mHitPoint.z < boxMax.z && mHitPoint.x > boxMin.x && mHitPoint.x < boxMax.x) return true;
if ( axis==Axis.Z && mHitPoint.x > boxMin.x && mHitPoint.x < boxMax.x && mHitPoint.y > boxMin.y && mHitPoint.y < boxMax.y) return true;
return false;
}
public BaseObject3D getPickedObject() {
return mPickedObject;
}
}