Performance improvement in BillboardNode

I did some work on a vertex shader that did camera aligned billboard rotations, and after looking at the jME code that does similar calculations, I discovered that I could contribute some code that will improve performance.



Here's the current method in jME to rotate a camera aligned BillboardNode:


private void rotateCameraAligned(Camera camera) {
        look.set(camera.getLocation()).subtractLocal(worldTranslation);
        look.normalizeLocal();

        float el = FastMath.asin(look.y);
        float az = FastMath.atan2(look.x, look.z);
        float elCos = FastMath.cos(el);
        float azCos = FastMath.cos(az);
        float elSin = FastMath.sin(el);
        float azSin = FastMath.sin(az);

        // compute the local orientation matrix for the billboard
        orient.m00 = azCos;
        orient.m01 = azSin * -elSin;
        orient.m02 = azSin * elCos;
        orient.m10 = 0;
        orient.m11 = elCos;
        orient.m12 = elSin;
        orient.m20 = -azSin;
        orient.m21 = azCos * -elSin;
        orient.m22 = azCos * elCos;

        // The billboard must be oriented to face the camera before it is
        // transformed into the world.
        worldRotation.apply(orient);
 }



Here's a replacement method that uses an additional sqrt but eliminates the 6 trig functions. (I added Vector3f xzp as a member variable.) The calculated orientation matrix is the same (well, to about 7 decimal places).

private void rotateCameraAligned(Camera camera) {
        look.set(cameraLocation).subtractLocal(worldTranslation);
        // The xzp vector is the projection of the look vector on the xz plane
   xzp.set(look.x, 0, look.z);
      
        look.normalizeLocal();
   xzp.normalizeLocal();

   // Calculate the cosine of the elevation angle
   float cosp = look.dot(xzp);
      
        // compute the local orientation matrix for the billboard
        orient.m00 = xzp.z;
        orient.m01 = xzp.x * -look.y;
        orient.m02 =  xzp.x * cosp;
        orient.m10 = 0;
        orient.m11 = cosp;
        orient.m12 = look.y;
        orient.m20 = -xzp.x;
        orient.m21 = xzp.z * -look.y;
        orient.m22 = xzp.z * cosp;      
       
        // The billboard must be oriented to face the camera before it is
        // transformed into the world.
        worldRotation.apply(orient);       
}




You'll probably need to provide a test to prove it really is an improvement :slight_smile:



I would like to say it looks good, but I really have no clue :stuck_out_tongue:

I should really be faster without all the trigonometric function calls.

Does this assume a Y up world view?

yeah, not sure what i was thinking, probably that I wish there was one math solution that would work under various up vectors (a lot of modeling programs default to z up.)



perhaps we just leave worldRotation alone if x and z are 0

Tested and added your efficiency changes for camera-aligned typed billboard nodes to CVS.  I removed the need for an extra vector (left and look are working vectors and can be used as needed) and also dropped a deprecated method (setType) as planned for the current release.



Thanks!