final Camera camera = Camera.getCurrentCamera();
// Compute the additional rotation required for the billboard to face
// the camera. To do this, the camera must be inverse-transformed into
// the model space of the billboard.
_look.set(camera.getLocation()).subtractLocal(_worldTransform.getTranslation());
final Matrix3 worldMatrix = Matrix3.fetchTempInstance().set(_worldTransform.getMatrix());
worldMatrix.applyPost(_look, _left); // coopt left for our own purposes.
final ReadOnlyVector3 scale = _worldTransform.getScale();
_left.divideLocal(scale);
// squared length of the camera projection in the xz-plane
final double lengthSquared = _left.getX() * _left.getX() + _left.getZ() * _left.getZ();
if (lengthSquared < MathUtils.EPSILON) {
// camera on the billboard axis, rotation not defined
return;
}
// unitize the projection
final double invLength = 1.0 / Math.sqrt(lengthSquared);
if (axis.getY() == 1) {
_left.setX(_left.getX() * invLength);
_left.setY(0.0);
_left.setZ(_left.getZ() * invLength);
// compute the local orientation matrix for the billboard
_orient.setValue(0, 0, _left.getZ());
_orient.setValue(0, 1, 0);
_orient.setValue(0, 2, _left.getX());
_orient.setValue(1, 0, 0);
_orient.setValue(1, 1, 1);
_orient.setValue(1, 2, 0);
_orient.setValue(2, 0, -_left.getX());
_orient.setValue(2, 1, 0);
_orient.setValue(2, 2, _left.getZ());
} else if (axis.getZ() == 1) {
_left.setX(_left.getX() * invLength);
_left.setY(_left.getY() * invLength);
_left.setZ(0.0);
// compute the local orientation matrix for the billboard
_orient.setValue(0, 0, _left.getY());
_orient.setValue(0, 1, _left.getX());
_orient.setValue(0, 2, 0);
_orient.setValue(1, 0, -_left.getY());
_orient.setValue(1, 1, _left.getX());
_orient.setValue(1, 2, 0);
_orient.setValue(2, 0, 0);
_orient.setValue(2, 1, 0);
_orient.setValue(2, 2, 1);
}
// The billboard must be oriented to face the camera before it is
// transformed into the world.
worldMatrix.multiplyLocal(_orient);
_worldTransform.setRotation(worldMatrix);
Matrix3.releaseTempInstance(worldMatrix);
}