EnvironmentHandler
EnvironmentInterface

Availability LightWave 6.0 | Component Layout | Header lwenviron.h

Environment handlers render the backdrop, the points at infinity that aren't covered by anything in the scene. This is the natural place to draw the sky, the distant horizon, or a procedural nebula in space.

Handler Activation Function

   XCALL_( int ) MyEnvironment( long version, GlobalFunc *global,
      LWEnvironmentHandler *local, void *serverData );

The local argument to an environment handler's activation function is an LWEnvironmentHandler.

   typedef struct st_LWEnvironmentHandler {
      LWInstanceFuncs *inst;
      LWItemFuncs     *item;
      LWRenderFuncs   *rend;
      LWError         (*evaluate) (LWInstance, LWEnvironmentAccess *);
      unsigned int    (*flags) (LWInstance);
   } LWEnvironmentHandler;

The first three members of this structure point to the standard handler functions. In addition to these, an environment handler provides an evaluation function and a flags function.

errmsg = evaluate( instance, access )
This is where the environment handler does its work. At each time step in the animation, the evaluation function is called for each affected pixel in the image. The access argument, described below, contains information about the environment to be colored.
f = flags( instance )
Returns flag bits combined using bitwise-or.

Interface Activation Function

   XCALL_( int ) MyInterface( long version, GlobalFunc *global,
      LWInterface *local, void *serverData );

This is the standard interface activation for handlers. An environment's non-modal xpanel interface is drawn on the Backdrop tab of the Effects panel.

Environment Access

This is the structure passed to the handler's evaluation function.

   typedef struct st_LWEnvironmentAccess {
      LWEnvironmentMode mode;
      double            h[2], p[2];
      LWDVector         dir;
      double            colRect[4][3];
      double            color[3];
   } LWEnvironmentAccess;
mode
The context of the evaluation. Currently this distinguishes between rendering (EHMODE_REAL) and lower quality previewing (EHMODE_PREVIEW).
h, p
The heading and pitch extents of a rectangular area of the backdrop. They're both expressed in radians. In preview mode, these form a bounding box in spherical coordinates of an area to be colored. They should be ignored in real mode.
dir
A vector pointing toward a point on the backdrop to be colored. Use this when evaluating in real mode.
colRect
In preview mode, this is where the evaluation function returns the color at the corners of the rectangular area defined by h and p. The preview display interpolates between these at points inside the rectangle.

colRect[0] gets the color of (h[0], p[0])
colRect[1]
gets the color of (h[0], p[1])
colRect[2]
gets the color of (h[1], p[0])
colRect[3]
gets the color of (h[1], p[1])

color
In real mode, this is where the evaluation function returns the color of the point defined by the direction vector dir.

Example

The horizon sample duplicates Layout's gradient backdrop settings. It can also produce a grid backdrop. Be sure to look for the haiku in the newTime function.

The following code can be used to draw a simple representation of the sky and ground that includes a disk for the sun.

Drawing the sun requires knowing whether a point on the backdrop is inside the sun's disk. The point's angular separation from the center of the sun must be less than the sun's angular radius. So we need a function for calculating the angular separation between two spherical coordinates.

   static double angsep( double h1, double p1, double h2, double p2 )
   {
      double cd;
   
      if ( h1 == h2 && p1 == p2 )
         return 0.0;
      
      cd = cos( p1 ) * cos( p2 ) * cos( fabs( h2 - h1 ))
         + sin( p1 ) * sin( p2 );
   
      /* catch small roundoff errors */
   
      if ( cd >  1.0 ) cd =  1.0;
      if ( cd < -1.0 ) cd = -1.0;
   
      return acos( cd );
   }

We'd also like to write a sampling function that uses the same representation for the point being sampled, regardless of whether it's called in preview or real mode, so we need to be able to convert the direction vector into spherical (heading and pitch) coordinates. If the vector points straight up or down, the heading is undefined, so we set it arbitrarily to 0. To avoid problems with roundoff, we say that the vector is straight up or down if the magnitude of the y component is within some epsilon of 1.0.

   static void vec2hp( LWDVector n, double *h, double *p )
   {
      *p = asin( -n[ 1 ] );

      if ( 1.0 - fabs( n[ 1 ] ) > 1e-5 ) {
         /* not straight up or down */
         *h = acos( n[ 2 ] / cos( *p ));
         if ( n[ 0 ] < 0.0 ) *h = 2 * PI - *h;
      }
      else *h = 0;
   }

The sampling function decides whether the point is in the sky, the ground, or the sun, and colors the point accordingly. If the backdrop point is below the horizon, the ground color is used. If both the point and the sun are above the horizon, the point is compared to the sun's position to decide whether the sun or the sky color is used. hsun and psun are the heading and pitch of the sun's position. In preview mode, the Manhattan distance between the point and the sun's center is sufficient, while in real mode we do the more expensive angular separation calculation.

   static void sample( MyInstance *inst, double h, double p,
      double *color, int mode )
   {
      int insun;

      if ( p >= 0.0 ) {
         color[ 0 ] = inst->gndcolor[ 0 ];
         color[ 1 ] = inst->gndcolor[ 1 ];
         color[ 2 ] = inst->gndcolor[ 2 ];
         return;
      }
   
      insun = inst->psun - inst->sunrad < 0.0;
      if ( insun ) {
         if ( mode == EHMODE_PREVIEW )
            insun =  ( fabs( h - inst->hsun ) < inst->sunrad )
                  && ( fabs( p - inst->psun ) < inst->sunrad );
         else
            insun = angsep( h, p, inst->hsun, inst->psun )
                  < inst->sunrad;
      }

      if ( insun ) {
         color[ 0 ] = inst->suncolor[ 0 ];
         color[ 1 ] = inst->suncolor[ 1 ];
         color[ 2 ] = inst->suncolor[ 2 ];
      }
      else {
         color[ 0 ] = inst->skycolor[ 0 ];
         color[ 1 ] = inst->skycolor[ 1 ];
         color[ 2 ] = inst->skycolor[ 2 ];
      }
   }

The evaluation function uses the sampling function to find the right color and returns the color to the renderer.

   XCALL_( static LWError )
   Evaluate( MyInstance *inst, LWEnvironmentAccess *access )
   {
      double h, p;

      switch ( access->mode )
      {
         case EHMODE_PREVIEW:
            sample( inst, access->h[ 0 ], access->p[ 0 ],
               access->colRect[ 0 ], access->mode );
            sample( inst, access->h[ 0 ], access->p[ 1 ],
               access->colRect[ 1 ], access->mode );
            sample( inst, access->h[ 1 ], access->p[ 0 ],
               access->colRect[ 2 ], access->mode );
            sample( inst, access->h[ 1 ], access->p[ 1 ],
               access->colRect[ 3 ], access->mode );
         break;

         case EHMODE_REAL:
            vec2hp( access->dir, &h, &p );
            sample( inst, h, p, access->color, access->mode );
            break;

         default:
            break;
      }

      return NULL;
   }