int
_tenFiberIntegrate_RK4(tenFiberContext *tfx, double forwDir[3]) {
  double loc[3], k1[3], k2[3], k3[3], k4[3], c1, c2, c3, c4, h;

  h = tfx->stepSize;
  c1 = h/6.0; c2 = h/3.0; c3 = h/3.0; c4 = h/6.0;

  _tenFiberStep[tfx->fiberType](tfx, k1);
  ELL_3V_SCALE_ADD2(loc, 1, tfx->wPos, 0.5*h, k1);
  if (_tenFiberProbe(tfx, loc)) return 1;
  _tenFiberStep[tfx->fiberType](tfx, k2);
  ELL_3V_SCALE_ADD2(loc, 1, tfx->wPos, 0.5*h, k2);
  if (_tenFiberProbe(tfx, loc)) return 1;
  _tenFiberStep[tfx->fiberType](tfx, k3);
  ELL_3V_SCALE_ADD2(loc, 1, tfx->wPos, h, k3);
  if (_tenFiberProbe(tfx, loc)) return 1;
  _tenFiberStep[tfx->fiberType](tfx, k4);

  ELL_3V_SET(forwDir,
             c1*k1[0] + c2*k2[0] + c3*k3[0] + c4*k4[0],
             c1*k1[1] + c2*k2[1] + c3*k3[1] + c4*k4[1],
             c1*k1[2] + c2*k2[2] + c3*k3[2] + c4*k4[2]);
  
  return 0;
}
int
echoRayIntx(echoIntx *intx, echoRay *ray, echoScene *scene,
            echoRTParm *parm, echoThreadState *tstate) {
  unsigned int idx;
  int ret;
  echoObject *kid;
  echoPos_t tmp;

  _echoVerbose = tstate->verbose;

  ret = AIR_FALSE;
  for (idx=0; idx<scene->rendArr->len; idx++) {
    kid = scene->rend[idx];
    if (_echoRayIntx[kid->type](intx, ray, kid, parm, tstate)) {
      ray->faar = intx->t;
      ret = AIR_TRUE;
      if (ray->shadow) {
        /* no point in testing any further */
        return ret;
      }
    }
  }
  if (ret) {
    /* being here means we're not a shadow ray */
    ELL_3V_SCALE_ADD2(intx->pos, 1, ray->from, intx->t, ray->dir);
    ELL_3V_SCALE(intx->view, -1, ray->dir);
    ELL_3V_NORM(intx->view, intx->view, tmp);
    /* this is needed for phong materials; for glass and metal,
       it is either used directly, or as a reference in fuzzification */
    _ECHO_REFLECT(intx->refl, intx->norm, intx->view, tmp);
  }

  return ret;
}
int
_echoRayIntx_Sphere(RAYINTX_ARGS(Sphere)) {
  echoPos_t t, A, B, C, r[3], dscr, pos[3], tmp;

  AIR_UNUSED(parm);
  AIR_UNUSED(tstate);
  ELL_3V_SUB(r, ray->from, obj->pos);
  A = ELL_3V_DOT(ray->dir, ray->dir);
  B = 2*ELL_3V_DOT(ray->dir, r);
  C = ELL_3V_DOT(r, r) - obj->rad*obj->rad;
  dscr = B*B - 4*A*C;
  if (dscr <= 0) {
    /* grazes or misses (most common case) */
    return AIR_FALSE;
  }
  /* else */
  dscr = sqrt(dscr);
  t = (-B - dscr)/(2*A);
  if (!AIR_IN_CL(ray->neer, t, ray->faar)) {
    t = (-B + dscr)/(2*A);
    if (!AIR_IN_CL(ray->neer, t, ray->faar)) {
      return AIR_FALSE;
    }
  }
  /* else one of the intxs is in [neer,faar] segment */
  intx->t = t;
  ELL_3V_SCALE_ADD2(pos, 1, ray->from, t, ray->dir);
  ELL_3V_SUB(intx->norm, pos, obj->pos);
  ELL_3V_NORM(intx->norm, intx->norm, tmp);
  intx->obj = OBJECT(obj);
  /* does NOT set u, v */
  return AIR_TRUE;
}
Beispiel #4
0
_hooverExtraContext *
_hooverExtraContextNew(hooverContext *ctx) {
  _hooverExtraContext *ec;
  
  ec = (_hooverExtraContext *)calloc(1, sizeof(_hooverExtraContext));
  if (ec) {
    _hooverLearnLengths(ec->volHLen, ec->voxLen, ctx);
    ELL_3V_SCALE_ADD2(ec->rayZero,
                      1.0, ctx->cam->from,
                      ctx->cam->vspNeer, ctx->cam->N);
  }
  return ec;
}
Beispiel #5
0
/*
** leaves v+3*0 untouched, but makes sure that v+3*0, v+3*1, and v+3*2
** are mutually orthogonal.  Also leaves the magnitudes of all
** vectors unchanged.
*/
void
_ell_3m_enforce_orthogonality(double v[9]) {
  double d00, d10, d11, d20, d21, d22, scl, tv[3];

  d00 = ELL_3V_DOT(v+3*0, v+3*0);
  d10 = ELL_3V_DOT(v+3*1, v+3*0);
  d11 = ELL_3V_DOT(v+3*1, v+3*1);
  ELL_3V_SCALE_ADD2(tv, 1, v+3*1, -d10/d00, v+3*0);
  scl = sqrt(d11/ELL_3V_DOT(tv, tv));
  ELL_3V_SCALE(v+3*1, scl, tv);
  d20 = ELL_3V_DOT(v+3*2, v+3*0);
  d21 = ELL_3V_DOT(v+3*2, v+3*1);
  d22 = ELL_3V_DOT(v+3*2, v+3*2);
  ELL_3V_SCALE_ADD3(tv, 1, v+3*2, -d20/d00, v+3*0, -d21/d00, v+3*1);
  scl = sqrt(d22/ELL_3V_DOT(tv, tv));
  ELL_3V_SCALE(v+3*2, scl, tv);
  return;
}
int
_echoRayIntx_Cylinder(RAYINTX_ARGS(Cylinder)) {
  echoPos_t A, B, C, aa, bb, cc, dd, ee, ff, dscr, cylt1, cylt2, t, tmax,
    twot[2], cylp1, cylp2, pos[3], tmp;
  int tidx, radi0, radi1, twocap[2], cap;

  AIR_UNUSED(parm);
  AIR_UNUSED(tstate);

  if (!_echoRayIntx_CubeSolid(&t, &tmax,
                              -1-ECHO_EPSILON, 1+ECHO_EPSILON,
                              -1-ECHO_EPSILON, 1+ECHO_EPSILON,
                              -1-ECHO_EPSILON, 1+ECHO_EPSILON, ray)) {
    return AIR_FALSE;
  }
  switch(obj->axis) {
  case 0:
    radi0 = 1; radi1 = 2;
    break;
  case 1:
    radi0 = 0; radi1 = 2;
    break;
  case 2: default:
    radi0 = 0; radi1 = 1;
    break;
  }
  aa = ray->dir[radi0];
  bb = ray->dir[radi1];
  ee = ray->dir[obj->axis];
  cc = ray->from[radi0];
  dd = ray->from[radi1];
  ff = ray->from[obj->axis];
  A = aa*aa + bb*bb;
  B = 2*(aa*cc + bb*dd);
  C = cc*cc + dd*dd - 1;
  dscr = B*B - 4*A*C;
  if (dscr <= 0) {
    /* infinite ray grazes or misses the infinite cylinder (not
       bounded to [-1,1] along cylinder's axis), so therefore the ray
       grazes or misses the actual cylinder */
    return AIR_FALSE;
  }
  /* else infinite ray intersects the infinite cylinder */
  dscr = sqrt(dscr);
  cylt1 = (-B - dscr)/(2*A);
  cylp1 = ff + cylt1*ee;
  cylt2 = (-B + dscr)/(2*A);
  cylp2 = ff + cylt2*ee;
  if ( (cylp1 <= -1 && cylp2 <= -1)
       || (cylp1 >= 1 && cylp2 >= 1) ) {
    /* both intersections with infinite cylinder lie on ONE side of the
       finite extent, so there can't be an intersection */
    return AIR_FALSE;
  }
  /* else infinite ray DOES intersect finite cylinder; we have to find
     if any of the intersections are in the [neer, faar] interval */
  tidx = 0;
  if (AIR_IN_CL(-1, cylp1, 1)) {
    twot[tidx] = cylt1;
    twocap[tidx] = 0;
    tidx++;
  }
  if (AIR_IN_CL(-1, cylp2, 1)) {
    twot[tidx] = cylt2;
    twocap[tidx] = 0;
    tidx++;
  }
  if (tidx < 2) {
    /* at least one of the two intersections is with the endcaps */
    t = (-ff - 1)/ee;
    ELL_3V_SCALE_ADD2(pos, 1, ray->from, t, ray->dir);
    aa = pos[radi0]; bb = pos[radi1]; cc = aa*aa + bb*bb;
    if (cc <= 1) {
      twot[tidx] = t;
      twocap[tidx] = 1;
      tidx++;
    }
    if (tidx < 2) {
      /* try other endcap */
      t = (-ff + 1)/ee;
      ELL_3V_SCALE_ADD2(pos, 1, ray->from, t, ray->dir);
      aa = pos[radi0]; bb = pos[radi1]; cc = aa*aa + bb*bb;
      if (cc <= 1) {
        twot[tidx] = t;
        twocap[tidx] = 1;
        tidx++;
      }
    }
  }
  if (!tidx) {
    return AIR_FALSE;
  }
  if (2 == tidx && twot[0] > twot[1]) {
    ELL_SWAP2(twot[0], twot[1], aa);
    ELL_SWAP2(twocap[0], twocap[1], cap);
  }
  t = twot[0];
  cap = twocap[0];
  if (!AIR_IN_CL(ray->neer, t, ray->faar)) {
    if (1 == tidx) {
      return AIR_FALSE;
    }
    t = twot[1];
    cap = twocap[1];
    if (!AIR_IN_CL(ray->neer, t, ray->faar)) {
      return AIR_FALSE;
    }
  }
  /* else one of the intxs is in [neer,faar] segment */
  intx->t = t;
  ELL_3V_SCALE_ADD2(pos, 1, ray->from, t, ray->dir);
  switch(obj->axis) {
  case 0:
    ELL_3V_SET(intx->norm,     cap*pos[0], (1-cap)*pos[1], (1-cap)*pos[2]);
    break;
  case 1:
    ELL_3V_SET(intx->norm, (1-cap)*pos[0],     cap*pos[1], (1-cap)*pos[2]);
    break;
  case 2: default:
    ELL_3V_SET(intx->norm, (1-cap)*pos[0], (1-cap)*pos[1],     cap*pos[2]);
    break;
  }
  ELL_3V_NORM(intx->norm, intx->norm, tmp);
  intx->obj = OBJECT(obj);
  /* does NOT set u, v */
  return AIR_TRUE;
}
int
_echoRayIntx_Superquad(RAYINTX_ARGS(Superquad)) {
  char me[]="_echoRayIntx_Superquad";
  echoPos_t TT=0, Tmin, Tmax, t0, t1, t2, t3, v1, v2, diff, tol,
    saveTmin, Vmin, Vmax, VV=0, dV, dVmin, dVmax, tmp,
    (*v)(echoPos_t, echoPos_t, echoPos_t,
         echoPos_t, echoPos_t),
    (*vg)(echoPos_t[3],
          echoPos_t, echoPos_t, echoPos_t,
          echoPos_t, echoPos_t),
    (*lvg)(echoPos_t[3],
           echoPos_t, echoPos_t, echoPos_t,
           echoPos_t, echoPos_t),
    from[3], grad[3], pos[3];  /* these two used only by macros */
  int iter;
  
  if (!_echoRayIntx_CubeSolid(&Tmin, &Tmax,
                              -1-2*ECHO_EPSILON, 1+2*ECHO_EPSILON,
                              -1-2*ECHO_EPSILON, 1+2*ECHO_EPSILON,
                              -1-2*ECHO_EPSILON, 1+2*ECHO_EPSILON, ray)) {
    return AIR_FALSE;
  }
  switch(obj->axis) {
    case 0:
      v = _echo_SuperquadX_v;
      vg = _echo_SuperquadX_vg;
      lvg = _echo_SuperquadX_lvg;
      break;
    case 1:
      v = _echo_SuperquadY_v;
      vg = _echo_SuperquadY_vg;
      lvg = _echo_SuperquadY_lvg;
      break;
    case 2: default:
      v = _echo_SuperquadZ_v;
      vg = _echo_SuperquadZ_vg;
      lvg = _echo_SuperquadZ_lvg;
      break;
  }
  if (tstate->verbose) {
    fprintf(stderr, "%s%s: Tmin, Tmax = %g, %g, ax = %d\n",
            _echoDot(tstate->depth), me, Tmin, Tmax, obj->axis);
  }

#define VAL(TT)                               \
  (ELL_3V_SCALE_ADD2(pos, 1, from, (TT), ray->dir),  \
   v(pos[0], pos[1], pos[2], obj->A, obj->B))

#define VALGRAD(VV, DV, TT)                                \
  ELL_3V_SCALE_ADD2(pos, 1, from, (TT), ray->dir);                \
  (VV) = vg(grad, pos[0], pos[1], pos[2], obj->A, obj->B); \
  (DV) = ELL_3V_DOT(grad, ray->dir)

#define LVALGRAD(VV, DV, TT)                                \
  ELL_3V_SCALE_ADD2(pos, 1, from, (TT), ray->dir);                 \
  (VV) = lvg(grad, pos[0], pos[1], pos[2], obj->A, obj->B); \
  (DV) = ELL_3V_DOT(grad, ray->dir)

#define RR 0.61803399
#define CC (1.0-RR)
#define SHIFT3(a,b,c,d) (a)=(b); (b)=(c); (c)=(d)
#define SHIFT2(a,b,c)   (a)=(b); (b)=(c)

  /* testing */
  ELL_3V_SCALE_ADD2(from, 1, ray->from, Tmin, ray->dir);
  saveTmin = Tmin;
  Tmin = 0;
  Tmax -= saveTmin;
  /*
  ELL_3V_COPY(from, ray->from);
  saveTmin = 0;
  */

  /* evaluate value and derivatives at Tmin and Tmax */
  VALGRAD(Vmin, dVmin, Tmin);
  VALGRAD(Vmax, dVmax, Tmax);

  /* if the segment start and end are both positive or both negative,
     and if the derivatives also don't change sign, there's no root.
     Also, due to convexity, if values at start and end are negative,
     then there is no root */
  if ( (Vmin*Vmax >= 0 && dVmin*dVmax >= 0) 
       || (Vmin <= 0 && Vmax <= 0) ) {
    return AIR_FALSE;
  }
  if (tstate->verbose) {
    fprintf(stderr, "%s%s: dVmin = %g, dVmax = %g, Vmin = %g, Vmax = %g\n",
            _echoDot(tstate->depth), me, dVmin, dVmax, Vmin, Vmax);
  }

  /* either the value changed sign, or the derivative changed sign, or
     both.  If, as is common, the derivatives changed sign, but the
     values didn't (which means they are both positive, due to a test
     above), we need to limit the interval by minimizing the value
     until either we see a negative value, or until the minimization
     converged.  Based on Golden Section Search, NR pg.401 */
  if (dVmin*dVmax < 0 && Vmin*Vmax >= 0) {
    t0 = Tmin;
    t1 = RR*Tmin + CC*Tmax;
    t2 = CC*Tmin + RR*Tmax;
    t3 = Tmax;
    v1 = VAL(t1);
    v2 = VAL(t2);
    if (tstate->verbose) {
      fprintf(stderr, "%s%s: \n"
              "     t0 = % 31.15f\n"
              "     t1 = % 31.15f  -> v1 = % 31.15f\n"
              "     t2 = % 31.15f  -> v2 = % 31.15f\n"
              "     t3 = % 31.15f\n",
              _echoDot(tstate->depth), me,
              t0, t1, v1, t2, v2, t3);
    }
    tol = sqrt(ECHO_POS_EPS);
    while ( (t3-t0 > tol*(t1+t2))         /* still haven't converged */
            && (v1 > 0 && v2 > 0) ) {     /* v1 and v2 both positive */
      diff = v2 - v1;
      if (v1 < v2) {
        SHIFT3(t3, t2, t1, CC*t0 + RR*t2);
        SHIFT2(v2, v1, VAL(t1));
      } else {
        SHIFT3(t0, t1, t2, RR*t1 + CC*t3);
        SHIFT2(v1, v2, VAL(t2));
      }
      if (tstate->verbose) {
        fprintf(stderr, "%s%s: %s ---> \n"
                "     t0 = % 31.15f\n"
                "     t1 = % 31.15f  -> v1 = % 31.15f\n"
                "     t2 = % 31.15f  -> v2 = % 31.15f\n"
                "     t3 = % 31.15f\n",
                _echoDot(tstate->depth), me,
                diff > 0 ? "v1 < v2" : "v1 > v2",
                t0, t1, v1, t2, v2, t3);
      }
    }
    if (v1 > 0 && v2 > 0) {
      /* the minimization stopped, yet both v1 and v2 are still positive,
         so there's no way we can have a root */
      if (tstate->verbose) {
        fprintf(stderr, "%s%s: minimization found no root\n",
                _echoDot(tstate->depth), me);
      }
      return AIR_FALSE;
    }
    /* else either v1 or v2 <= 0, so there is a root (actually two).
       By construction, f(t0) is positive, so we can now bracket the
       root between t0 and t1 or t2, whichever one is associated with
       a smaller value */
    Tmin = t0;
    Tmax = v1 < v2 ? t1 : t2;  /* HEY: shouldn't I just be using whichever one is closer? */
    Vmin = VAL(Tmin);
    Vmax = VAL(Tmax);
  }

  /* the value isn't necessarily monotonic between Tmin and Tmax, but
     we know that there is only one root. Find it with newton-raphson,
     using the log of function, both for values and for derivatives */
  iter = 0;
  TT = (Tmin + Tmax)/2;
  LVALGRAD(VV, dV, TT);

  while (iter < parm->sqNRI && AIR_ABS(VV) > parm->sqTol
         && AIR_EXISTS(VV) && AIR_EXISTS(dV)) {
    if (tstate->verbose) {
      fprintf(stderr, "%s%s: iter = %d: TT = %g, VV = %g, dV = %g\n",
              _echoDot(tstate->depth), me, iter, TT, VV, dV);
    }
    TT -= VV/dV;
    if (!AIR_IN_OP(Tmin, TT, Tmax)) {
      /* newton-raphson sent us flying out of bounds; find a tighter
         [Tmin,Tmax] bracket with bisection and try again */
      TT = (Tmin + Tmax)/2;
      VV = VAL(TT);
      if (Vmin*VV < 0) {
        Tmax = TT;
        Vmax = VV;
      } else {
        Tmin = TT;
        Vmin = VV;
      }
      TT = (Tmin + Tmax)/2;
    }
    LVALGRAD(VV, dV, TT);
    iter++;
  }

  if (!( AIR_EXISTS(VV) && AIR_EXISTS(dV) )) {
    /* we bailed out of the loop above because
       we got screwed by numerical errors
       --> pretend that there was no intersection,
       and HEY this will have to be debugged later */
    return AIR_FALSE;
  }

  /* else we succeedded in finding the intersection */
  intx->t = TT + saveTmin;
  VALGRAD(VV, dV, TT);   /* puts gradient into grad */
  ELL_3V_NORM(intx->norm, grad, tmp);
  intx->obj = OBJECT(obj);
  /* set in intx:
     yes: t, norm
     no: u, v
  */
  return AIR_TRUE;
}
Beispiel #8
0
void *
_hooverThreadBody(void *_arg) {
  _hooverThreadArg *arg;
  void *thread;
  int ret,               /* to catch return values from callbacks */
    sampleI,             /* which sample we're on */
    inside,              /* we're inside the volume */
    vI, uI;              /* integral coords in image */
  double tmp,
    mm,                  /* lowest position in index space, for all axes */
    Mx, My, Mz,          /* highest position in index space on each axis */
    u, v,                /* floating-point coords in image */
    uvScale,             /* how to scale (u,v) to go from image to 
                            near plane, according to ortho or perspective */
    lx, ly, lz,          /* half edge-lengths of volume */
    rayLen=0,            /* length of segment formed by ray line intersecting
                            the near and far clipping planes */
    rayT,                /* current position along ray (world-space) */
    rayDirW[3],          /* unit-length ray direction (world-space) */
    rayDirI[3],          /* rayDirW transformed into index space;
                            not unit length, but a unit change in
                            world space along rayDirW translates to
                            this change in index space along rayDirI */
    rayPosW[3],          /* current ray location (world-space) */
    rayPosI[3],          /* current ray location (index-space) */
    rayStartW[3],        /* ray start on near plane (world-space) */
    rayStartI[3],        /* ray start on near plane (index-space) */
    rayStep,             /* distance between samples (world-space) */
    vOff[3], uOff[3];    /* offsets in arg->ec->wU and arg->ec->wV
                            directions towards start of ray */

  arg = (_hooverThreadArg *)_arg;
  if ( (ret = (arg->ctx->threadBegin)(&thread, 
                                      arg->render, 
                                      arg->ctx->user,
                                      arg->whichThread)) ) {
    arg->errCode = ret;
    arg->whichErr = hooverErrThreadBegin;
    return arg;
  }
  lx = arg->ec->volHLen[0];
  ly = arg->ec->volHLen[1];
  lz = arg->ec->volHLen[2];
  if (nrrdCenterNode == arg->ctx->volCentering) {
    mm = 0;
    Mx = arg->ctx->volSize[0]-1;
    My = arg->ctx->volSize[1]-1;
    Mz = arg->ctx->volSize[2]-1;
  } else {
    mm = -0.5;
    Mx = arg->ctx->volSize[0]-0.5;
    My = arg->ctx->volSize[1]-0.5;
    Mz = arg->ctx->volSize[2]-0.5;
  }
  
  if (arg->ctx->cam->orthographic) {
    ELL_3V_COPY(rayDirW, arg->ctx->cam->N);
    rayDirI[0] = AIR_DELTA(-lx, rayDirW[0], lx, mm, Mx);
    rayDirI[1] = AIR_DELTA(-ly, rayDirW[1], ly, mm, My);
    rayDirI[2] = AIR_DELTA(-lz, rayDirW[2], lz, mm, Mz);
    rayLen = arg->ctx->cam->vspFaar - arg->ctx->cam->vspNeer;
    uvScale = 1.0;
  } else {
    uvScale = arg->ctx->cam->vspNeer/arg->ctx->cam->vspDist;
  }

  while (1) {
    /* the work assignment is simply the next scanline to be rendered:
       the result of all this is setting vI */
    if (arg->ctx->workMutex) {
      airThreadMutexLock(arg->ctx->workMutex);
    }
    vI = arg->ctx->workIdx;
    if (arg->ctx->workIdx < arg->ctx->imgSize[1]) {
      arg->ctx->workIdx += 1;
    }
    if (arg->ctx->workMutex) {
      airThreadMutexUnlock(arg->ctx->workMutex);
    }
    if (vI == arg->ctx->imgSize[1]) {
      /* we're done! */
      break;
    }

    if (nrrdCenterCell == arg->ctx->imgCentering) {
      v = uvScale*AIR_AFFINE(-0.5, vI, arg->ctx->imgSize[1]-0.5,
                             arg->ctx->cam->vRange[0],
                             arg->ctx->cam->vRange[1]);
    } else {
      v = uvScale*AIR_AFFINE(0.0, vI, arg->ctx->imgSize[1]-1.0,
                             arg->ctx->cam->vRange[0],
                             arg->ctx->cam->vRange[1]);
    }
    ELL_3V_SCALE(vOff, v, arg->ctx->cam->V);
    for (uI=0; uI<arg->ctx->imgSize[0]; uI++) {
      if (nrrdCenterCell == arg->ctx->imgCentering) {
        u = uvScale*AIR_AFFINE(-0.5, uI, arg->ctx->imgSize[0]-0.5,
                               arg->ctx->cam->uRange[0],
                               arg->ctx->cam->uRange[1]);
      } else {
        u = uvScale*AIR_AFFINE(0.0, uI, arg->ctx->imgSize[0]-1.0,
                               arg->ctx->cam->uRange[0],
                               arg->ctx->cam->uRange[1]);
      }
      ELL_3V_SCALE(uOff, u, arg->ctx->cam->U);
      ELL_3V_ADD3(rayStartW, uOff, vOff, arg->ec->rayZero);
      rayStartI[0] = AIR_AFFINE(-lx, rayStartW[0], lx, mm, Mx);
      rayStartI[1] = AIR_AFFINE(-ly, rayStartW[1], ly, mm, My);
      rayStartI[2] = AIR_AFFINE(-lz, rayStartW[2], lz, mm, Mz);
      if (!arg->ctx->cam->orthographic) {
        ELL_3V_SUB(rayDirW, rayStartW, arg->ctx->cam->from);
        ELL_3V_NORM(rayDirW, rayDirW, tmp);
        rayDirI[0] = AIR_DELTA(-lx, rayDirW[0], lx, mm, Mx);
        rayDirI[1] = AIR_DELTA(-ly, rayDirW[1], ly, mm, My);
        rayDirI[2] = AIR_DELTA(-lz, rayDirW[2], lz, mm, Mz);
        rayLen = ((arg->ctx->cam->vspFaar - arg->ctx->cam->vspNeer)/
                  ELL_3V_DOT(rayDirW, arg->ctx->cam->N));
      }
      if ( (ret = (arg->ctx->rayBegin)(thread,
                                       arg->render,
                                       arg->ctx->user,
                                       uI, vI, rayLen,
                                       rayStartW, rayStartI,
                                       rayDirW, rayDirI)) ) {
        arg->errCode = ret;
        arg->whichErr = hooverErrRayBegin;
        return arg;
      }
      
      sampleI = 0;
      rayT = 0;
      while (1) {
        ELL_3V_SCALE_ADD2(rayPosW, 1.0, rayStartW, rayT, rayDirW);
        ELL_3V_SCALE_ADD2(rayPosI, 1.0, rayStartI, rayT, rayDirI);
        inside = (AIR_IN_CL(mm, rayPosI[0], Mx) &&
                  AIR_IN_CL(mm, rayPosI[1], My) &&
                  AIR_IN_CL(mm, rayPosI[2], Mz));
        rayStep = (arg->ctx->sample)(thread,
                                     arg->render,
                                     arg->ctx->user,
                                     sampleI, rayT,
                                     inside,
                                     rayPosW, rayPosI);
        if (!AIR_EXISTS(rayStep)) {
          /* sampling failed */
          arg->errCode = 0;
          arg->whichErr = hooverErrSample;
          return arg;
        }
        if (!rayStep) {
          /* ray decided to finish itself */
          break;
        } 
        /* else we moved to a new location along the ray */
        rayT += rayStep;
        if (!AIR_IN_CL(0, rayT, rayLen)) {
          /* ray stepped outside near-far clipping region, its done. */
          break;
        }
        sampleI++;
      }
      
      if ( (ret = (arg->ctx->rayEnd)(thread,
                                     arg->render,
                                     arg->ctx->user)) ) {
        arg->errCode = ret;
        arg->whichErr = hooverErrRayEnd;
        return arg;
      }
    }  /* end this scanline */
  } /* end while(1) assignment of scanlines */

  if ( (ret = (arg->ctx->threadEnd)(thread,
                                    arg->render,
                                    arg->ctx->user)) ) {
    arg->errCode = ret;
    arg->whichErr = hooverErrThreadEnd;
    return arg;
  }
  
  /* returning NULL actually indicates that there was NOT an error */
  return NULL;
}
Beispiel #9
0
/*
** Do asynchronous update of positions in "npos', based on force
** calculations wherein the distances are normalized "edge".  Using a
** small "edge" allows forces to either underflow to zero, or be
** finite, instead of exploding to infinity, for high exponents.
**
** The smallest seen edge length is recorded in "*edgeMin", which is
** initialized to the given "edge".  This allows, for example, the
** caller to try again with a smaller edge normalization.
**
** The mean velocity of the points through the update is recorded in
** "*meanVel".
**
** Based on the observation that when using large exponents, numerical
** difficulties arise from the (force-based) update of the positions
** of the two (or few) closest particles, this function puts a speed
** limit (variable "limit") on the distance a particle may move during
** update, expressed as a fraction of the normalizing edge length.
** "limit" has been set heuristically, according to the exponent (we
** have to clamp speeds more aggresively with higher exponents), as
** well as (even more heuristically) according to the number of times
** the step size has been decreased.  This latter factor has to be
** bounded, so that the update is not unnecessarily bounded when the
** step size gets very small at the last stages of computation.
** Without the step-size-based speed limit, the step size would
** sometimes (e.g. num=200, expo=300) have to reduced to a miniscule
** value, which slows subsequent convergence terribly.
**
** this function is not static, though it could be, so that mac's
** "Sampler" app can profile this
*/
int
_tenGradientUpdate(double *meanVel, double *edgeMin,
                   Nrrd *npos, double edge, tenGradientParm *tgparm) {
  /* static const char me[]="_tenGradientUpdate"; */
  double *pos, newpos[3], grad[3], ngrad[3],
    dir[3], len, rep, step, diff[3], limit, expo;
  int num, ii, jj, E;

  E = 0;
  pos = AIR_CAST(double *, npos->data);
  num = AIR_UINT(npos->axis[1].size);
  *meanVel = 0;
  *edgeMin = edge;
  expo = tgparm->expo ? tgparm->expo : tgparm->expo_d;
  limit = expo*AIR_MIN(sqrt(expo),
                       log(1 + tgparm->initStep/tgparm->step));
  for (ii=0; ii<num; ii++) {
    ELL_3V_SET(grad, 0, 0, 0);
    for (jj=0; jj<num; jj++) {
      if (ii == jj) {
        continue;
      }
      ELL_3V_SUB(dir, pos + 3*ii, pos + 3*jj);
      ELL_3V_NORM(dir, dir, len);
      *edgeMin = AIR_MIN(*edgeMin, len);
      if (tgparm->expo) {
        rep = airIntPow(edge/len, tgparm->expo+1);
      } else {
        rep = pow(edge/len, tgparm->expo_d+1);
      }
      ELL_3V_SCALE_INCR(grad, rep/num, dir);
      if (!tgparm->single) {
        ELL_3V_ADD2(dir, pos + 3*ii, pos + 3*jj);
        ELL_3V_NORM(dir, dir, len);
        *edgeMin = AIR_MIN(*edgeMin, len);
        if (tgparm->expo) {
          rep = airIntPow(edge/len, tgparm->expo+1);
        } else {
          rep = pow(edge/len, tgparm->expo_d+1);
        }
        ELL_3V_SCALE_INCR(grad, rep/num, dir);
      }
    }
    ELL_3V_NORM(ngrad, grad, len);
    if (!( AIR_EXISTS(len) )) {
      /* things blew up, either in incremental force
         additions, or in the attempt at normalization */
      E = 1;
      *meanVel = AIR_NAN;
      break;
    }
    if (0 == len) {
      /* if the length of grad[] underflowed to zero, we can
         legitimately zero out ngrad[] */
      ELL_3V_SET(ngrad, 0, 0, 0);
    }
    step = AIR_MIN(len*tgparm->step, edge/limit);
    ELL_3V_SCALE_ADD2(newpos,
                      1.0, pos + 3*ii,
                      step, ngrad);
    ELL_3V_NORM(newpos, newpos, len);
    ELL_3V_SUB(diff, pos + 3*ii, newpos);
    *meanVel += ELL_3V_LEN(diff);
    ELL_3V_COPY(pos + 3*ii, newpos);
  }
  *meanVel /= num;

  return E;
}
Beispiel #10
0
/*
******** limnCameraPathMake
**
** uses limnSplines to do camera paths based on key-frames
**
** output: cameras at all "numFrames" frames are set in the
** PRE-ALLOCATED array of output cameras, "cam".
**
** input:
** keycam: array of keyframe cameras
** time: times associated with the key frames
** ---> both of these arrays are length "numKeys" <---
** trackWhat: takes values from the limnCameraPathTrack* enum
** quatType: spline to control camera orientations. This is needed for
**          tracking at or from, but not needed for limnCameraPathTrackBoth.
**          This is the only limnSplineTypeSpec* argument that can be NULL.
** posType: spline to control whichever of from, at, and up are needed for
**          the given style of tracking.
** distType: spline to control neer, faar, dist: positions of near clipping,
**          far clipping, and image plane, as well as the
**          distance between from and at (which is used if not doing
**          limnCameraPathTrackBoth)
** viewType: spline to control fov (and aspect, if you're crazy)
**
** NOTE: The "atRelative", "orthographic", and "rightHanded" fields
** are copied from keycam[0] into all output cam[i], but you still need
** to correctly set them for all keycam[i] for limnCameraUpdate to work
** as expected.  Also, for the sake of simplicity, this function only works
** with fov and aspect, instead of {u,v}Range, and hence both "fov" and
** "aspect" need to set in *all* the keycams, even if neither of them
** ever changes!
*/
int
limnCameraPathMake(limnCamera *cam, int numFrames,
                   limnCamera *keycam, double *time, int numKeys,
                   int trackWhat,
                   limnSplineTypeSpec *quatType,
                   limnSplineTypeSpec *posType,
                   limnSplineTypeSpec *distType,
                   limnSplineTypeSpec *viewType) {
  static const char me[]="limnCameraPathMake";
  char which[AIR_STRLEN_MED];
  airArray *mop;
  Nrrd *nquat, *nfrom, *natpt, *nupvc, *ndist, *nfova, *ntime, *nsample;
  double fratVec[3], *quat, *from, *atpt, *upvc, *dist, *fova,
    W2V[9], N[3], fratDist;
  limnSpline *timeSpline, *quatSpline, *fromSpline, *atptSpline, *upvcSpline,
    *distSpline, *fovaSpline;
  limnSplineTypeSpec *timeType;
  int ii, E;

  if (!( cam && keycam && time && posType && distType && viewType )) {
    biffAddf(LIMN, "%s: got NULL pointer", me);
    return 1;
  }
  if (!( AIR_IN_OP(limnCameraPathTrackUnknown, trackWhat,
                   limnCameraPathTrackLast) )) {
    biffAddf(LIMN, "%s: trackWhat %d not in valid range [%d,%d]", me,
             trackWhat, limnCameraPathTrackUnknown+1,
             limnCameraPathTrackLast-1);
    return 1;
  }
  if (limnCameraPathTrackBoth != trackWhat && !quatType) {
    biffAddf(LIMN, "%s: need the quaternion limnSplineTypeSpec if not "
             "doing trackBoth", me);
    return 1;
  }

  /* create and allocate nrrds.  For the time being, we're allocating
     more different nrrds, and filling their contents, than we need
     to-- nquat is not needed if we're doing limnCameraPathTrackBoth,
     for example.  However, we do make an effort to only do the spline
     evaluation on the things we actually need to know. */
  mop = airMopNew();
  airMopAdd(mop, nquat = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
  airMopAdd(mop, nfrom = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
  airMopAdd(mop, natpt = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
  airMopAdd(mop, nupvc = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
  airMopAdd(mop, ndist = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
  airMopAdd(mop, nfova = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
  airMopAdd(mop, ntime = nrrdNew(), (airMopper)nrrdNix, airMopAlways);
  if (nrrdWrap_va(ntime, time, nrrdTypeDouble, 1,
                  AIR_CAST(size_t, numKeys))) {
    biffMovef(LIMN, NRRD, "%s: trouble wrapping time values", me);
    airMopError(mop); return 1;
  }
  airMopAdd(mop, nsample = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
  timeType = limnSplineTypeSpecNew(limnSplineTypeTimeWarp);
  airMopAdd(mop, timeType, (airMopper)limnSplineTypeSpecNix, airMopAlways);
  if (nrrdMaybeAlloc_va(nquat, nrrdTypeDouble, 2,
                        AIR_CAST(size_t, 4), AIR_CAST(size_t, numKeys))
      || nrrdMaybeAlloc_va(nfrom, nrrdTypeDouble, 2,
                           AIR_CAST(size_t, 3), AIR_CAST(size_t, numKeys))
      || nrrdMaybeAlloc_va(natpt, nrrdTypeDouble, 2,
                           AIR_CAST(size_t, 3), AIR_CAST(size_t, numKeys))
      || nrrdMaybeAlloc_va(nupvc, nrrdTypeDouble, 2,
                           AIR_CAST(size_t, 3), AIR_CAST(size_t, numKeys))
      || nrrdMaybeAlloc_va(ndist, nrrdTypeDouble, 2,
                           AIR_CAST(size_t, 4), AIR_CAST(size_t, numKeys))
      || nrrdMaybeAlloc_va(nfova, nrrdTypeDouble, 2,
                           AIR_CAST(size_t, 2), AIR_CAST(size_t, numKeys))) {
    biffMovef(LIMN, NRRD, "%s: couldn't allocate buffer nrrds", me);
    airMopError(mop); return 1;
  }
  quat = (double*)(nquat->data);
  from = (double*)(nfrom->data);
  atpt = (double*)(natpt->data);
  upvc = (double*)(nupvc->data);
  dist = (double*)(ndist->data);
  fova = (double*)(nfova->data);

  /* check cameras, and put camera information into nrrds */
  for (ii=0; ii<numKeys; ii++) {
    if (limnCameraUpdate(keycam + ii)) {
      biffAddf(LIMN, "%s: trouble with camera at keyframe %d\n", me, ii);
      airMopError(mop); return 1;
    }
    if (!( AIR_EXISTS(keycam[ii].fov) && AIR_EXISTS(keycam[ii].aspect) )) {
      biffAddf(LIMN, "%s: fov, aspect not both defined on keyframe %d",
               me, ii);
      airMopError(mop); return 1;
    }
    ell_4m_to_q_d(quat + 4*ii, keycam[ii].W2V);
    if (ii) {
      if (0 > ELL_4V_DOT(quat + 4*ii, quat + 4*(ii-1))) {
        ELL_4V_SCALE(quat + 4*ii, -1, quat + 4*ii);
      }
    }
    ELL_3V_COPY(from + 3*ii, keycam[ii].from);
    ELL_3V_COPY(atpt + 3*ii, keycam[ii].at);
    ELL_3V_COPY(upvc + 3*ii, keycam[ii].up);
    ELL_3V_SUB(fratVec, keycam[ii].from, keycam[ii].at);
    fratDist = ELL_3V_LEN(fratVec);
    ELL_4V_SET(dist + 4*ii, fratDist,
               keycam[ii].neer, keycam[ii].dist, keycam[ii].faar);
    ELL_2V_SET(fova + 2*ii, keycam[ii].fov, keycam[ii].aspect);
  }

  /* create splines from nrrds */
  if (!( (strcpy(which, "quaternion"), quatSpline =
          limnSplineCleverNew(nquat, limnSplineInfoQuaternion, quatType))
         && (strcpy(which, "from point"), fromSpline =
             limnSplineCleverNew(nfrom, limnSplineInfo3Vector, posType))
         && (strcpy(which, "at point"), atptSpline =
             limnSplineCleverNew(natpt, limnSplineInfo3Vector, posType))
         && (strcpy(which, "up vector"), upvcSpline =
             limnSplineCleverNew(nupvc, limnSplineInfo3Vector, posType))
         && (strcpy(which, "plane distances"), distSpline =
             limnSplineCleverNew(ndist, limnSplineInfo4Vector, distType))
         && (strcpy(which, "field-of-view"), fovaSpline =
             limnSplineCleverNew(nfova, limnSplineInfo2Vector, viewType))
         && (strcpy(which, "time warp"), timeSpline =
             limnSplineCleverNew(ntime, limnSplineInfoScalar, timeType)) )) {
    biffAddf(LIMN, "%s: trouble creating %s spline", me, which);
    airMopError(mop); return 1;
  }
  airMopAdd(mop, quatSpline, (airMopper)limnSplineNix, airMopAlways);
  airMopAdd(mop, fromSpline, (airMopper)limnSplineNix, airMopAlways);
  airMopAdd(mop, atptSpline, (airMopper)limnSplineNix, airMopAlways);
  airMopAdd(mop, upvcSpline, (airMopper)limnSplineNix, airMopAlways);
  airMopAdd(mop, distSpline, (airMopper)limnSplineNix, airMopAlways);
  airMopAdd(mop, fovaSpline, (airMopper)limnSplineNix, airMopAlways);
  airMopAdd(mop, timeSpline, (airMopper)limnSplineNix, airMopAlways);

  /* evaluate splines */
  E = AIR_FALSE;
  if (!E) E |= limnSplineSample(nsample, timeSpline,
                                limnSplineMinT(timeSpline), numFrames,
                                limnSplineMaxT(timeSpline));
  quat = NULL;
  from = NULL;
  atpt = NULL;
  upvc = NULL;
  switch(trackWhat) {
  case limnCameraPathTrackAt:
    if (!E) E |= limnSplineNrrdEvaluate(natpt, atptSpline, nsample);
    if (!E) atpt = (double*)(natpt->data);
    if (!E) E |= limnSplineNrrdEvaluate(nquat, quatSpline, nsample);
    if (!E) quat = (double*)(nquat->data);
    break;
  case limnCameraPathTrackFrom:
    if (!E) E |= limnSplineNrrdEvaluate(nfrom, fromSpline, nsample);
    if (!E) from = (double*)(nfrom->data);
    if (!E) E |= limnSplineNrrdEvaluate(nquat, quatSpline, nsample);
    if (!E) quat = (double*)(nquat->data);
    break;
  case limnCameraPathTrackBoth:
    if (!E) E |= limnSplineNrrdEvaluate(nfrom, fromSpline, nsample);
    if (!E) from = (double*)(nfrom->data);
    if (!E) E |= limnSplineNrrdEvaluate(natpt, atptSpline, nsample);
    if (!E) atpt = (double*)(natpt->data);
    if (!E) E |= limnSplineNrrdEvaluate(nupvc, upvcSpline, nsample);
    if (!E) upvc = (double*)(nupvc->data);
    break;
  }
  dist = NULL;
  if (!E) E |= limnSplineNrrdEvaluate(ndist, distSpline, nsample);
  if (!E) dist = (double*)(ndist->data);
  fova = NULL;
  if (!E) E |= limnSplineNrrdEvaluate(nfova, fovaSpline, nsample);
  if (!E) fova = (double*)(nfova->data);
  if (E) {
    biffAddf(LIMN, "%s: trouble evaluating splines", me);
    airMopError(mop); return 1;
  }

  /* copy information from nrrds back into cameras */
  for (ii=0; ii<numFrames; ii++) {
    cam[ii].atRelative = keycam[0].atRelative;
    cam[ii].orthographic = keycam[0].orthographic;
    cam[ii].rightHanded = keycam[0].rightHanded;
    if (limnCameraPathTrackBoth == trackWhat) {
      ELL_3V_COPY(cam[ii].from, from + 3*ii);
      ELL_3V_COPY(cam[ii].at, atpt + 3*ii);
      ELL_3V_COPY(cam[ii].up, upvc + 3*ii);
    } else {
      fratDist = (dist + 4*ii)[0];
      ell_q_to_3m_d(W2V, quat + 4*ii);
      ELL_3MV_ROW1_GET(cam[ii].up, W2V);
      if (cam[ii].rightHanded) {
        ELL_3V_SCALE(cam[ii].up, -1, cam[ii].up);
      }
      ELL_3MV_ROW2_GET(N, W2V);
      if (limnCameraPathTrackFrom == trackWhat) {
        ELL_3V_COPY(cam[ii].from, from + 3*ii);
        ELL_3V_SCALE_ADD2(cam[ii].at, 1.0, cam[ii].from, fratDist, N);
      } else {
        ELL_3V_COPY(cam[ii].at, atpt + 3*ii);
        ELL_3V_SCALE_ADD2(cam[ii].from, 1.0, cam[ii].at, -fratDist, N);
      }
    }
    cam[ii].neer = (dist + 4*ii)[1];
    cam[ii].dist = (dist + 4*ii)[2];
    cam[ii].faar = (dist + 4*ii)[3];
    cam[ii].fov = (fova + 2*ii)[0];
    cam[ii].aspect = (fova + 2*ii)[1];
    if (limnCameraUpdate(cam + ii)) {
      biffAddf(LIMN, "%s: trouble with output camera %d\n", me, ii);
      airMopError(mop); return 1;
    }
  }

  airMopOkay(mop);
  return 0;
}
Beispiel #11
0
double
miteSample(miteThread *mtt, miteRender *mrr, miteUser *muu,
           int num, double rayT, int inside,
           double samplePosWorld[3],
           double samplePosIndex[3]) {
  static const char me[]="miteSample";
  mite_t R, G, B, A;
  double *NN;
  double NdotV, kn[3], knd[3], ref[3], len, *dbg=NULL;

  if (!inside) {
    return mtt->rayStep;
  }

  if (mtt->skip) {
    /* we have one verbose pixel, but we're not on it */
    return 0.0;
  }

  /* early ray termination */
  if (1-mtt->TT >= muu->opacNear1) {
    mtt->TT = 0.0;
    return 0.0;
  }

  /* set (fake) view based on fake from */
  if (AIR_EXISTS(muu->fakeFrom[0])) {
    ELL_3V_SUB(mtt->V, samplePosWorld, muu->fakeFrom);
    ELL_3V_NORM(mtt->V, mtt->V, len);
  }

  /* do probing at this location to determine values of everything
     that might appear in the txf domain */
  if (gageProbe(mtt->gctx,
                samplePosIndex[0],
                samplePosIndex[1],
                samplePosIndex[2])) {
    biffAddf(MITE, "%s: gage trouble: %s (%d)", me,
             mtt->gctx->errStr, mtt->gctx->errNum);
    return AIR_NAN;
  }

  if (mrr->queryMiteNonzero) {
    /* There is some optimal trade-off between slowing things down
       with too many branches on all possible checks of queryMite,
       and slowing things down with doing the work of setting them all.
       This code has not been profiled whatsoever */
    mtt->directAnsMiteVal[miteValXw][0] = samplePosWorld[0];
    mtt->directAnsMiteVal[miteValXi][0] = samplePosIndex[0];
    mtt->directAnsMiteVal[miteValYw][0] = samplePosWorld[1];
    mtt->directAnsMiteVal[miteValYi][0] = samplePosIndex[1];
    mtt->directAnsMiteVal[miteValZw][0] = samplePosWorld[2];
    mtt->directAnsMiteVal[miteValZi][0] = samplePosIndex[2];
    mtt->directAnsMiteVal[miteValRw][0] = ELL_3V_LEN(samplePosWorld);
    mtt->directAnsMiteVal[miteValRi][0] = ELL_3V_LEN(samplePosIndex);
    mtt->directAnsMiteVal[miteValTw][0] = rayT;
    mtt->directAnsMiteVal[miteValTi][0] = num;
    ELL_3V_COPY(mtt->directAnsMiteVal[miteValView], mtt->V);
    NN = mtt->directAnsMiteVal[miteValNormal];
    if (mtt->_normal) {
      if (1 == muu->normalSide) {
        ELL_3V_SCALE(NN, -1, mtt->_normal);
      } else {
        ELL_3V_COPY(NN, mtt->_normal);
      }
    }

    if ((GAGE_QUERY_ITEM_TEST(mrr->queryMite, miteValNdotV)
         || GAGE_QUERY_ITEM_TEST(mrr->queryMite, miteValNdotL)
         || GAGE_QUERY_ITEM_TEST(mrr->queryMite, miteValVrefN))) {
      mtt->directAnsMiteVal[miteValNdotV][0] = ELL_3V_DOT(NN, mtt->V);
      mtt->directAnsMiteVal[miteValNdotL][0] =
        ELL_3V_DOT(NN, muu->lit->dir[0]);
      if (!muu->normalSide) {
        mtt->directAnsMiteVal[miteValNdotV][0] =
          AIR_ABS(mtt->directAnsMiteVal[miteValNdotV][0]);
        mtt->directAnsMiteVal[miteValNdotL][0] =
          AIR_ABS(mtt->directAnsMiteVal[miteValNdotL][0]);
      }
      NdotV = mtt->directAnsMiteVal[miteValNdotV][0];
      ELL_3V_SCALE_ADD2(ref, 2*NdotV, NN, -1, mtt->V);
      ELL_3V_NORM(mtt->directAnsMiteVal[miteValVrefN], ref, len);
    }

    if (GAGE_QUERY_ITEM_TEST(mrr->queryMite, miteValGTdotV)) {
      ELL_3MV_MUL(kn, mtt->nPerp, mtt->V);
      ELL_3V_NORM(kn, kn, len);
      ELL_3MV_MUL(knd, mtt->geomTens, kn);
      mtt->directAnsMiteVal[miteValGTdotV][0] = ELL_3V_DOT(knd, kn);
    }
  }


  /* initialize txf range quantities, and apply all txfs */
  if (mtt->verbose) {
    muu->debugIdx = airArrayLenIncr(muu->debugArr, muu->ndebug->axis[0].size);
  }

  memcpy(mtt->range, muu->rangeInit, MITE_RANGE_NUM*sizeof(mite_t));
  _miteStageRun(mtt, muu);

  /* if there's opacity, do shading and compositing */
  if (mtt->range[miteRangeAlpha]) {
    /* fprintf(stderr, "%s: mtt->TT = %g\n", me, mtt->TT); */
    /*
    if (mtt->verbose) {
      fprintf(stderr, "%s: before compositing: RGBT = %g,%g,%g,%g\n",
              me, mtt->RR, mtt->GG, mtt->BB, mtt->TT);
    }
    */
    _miteRGBACalc(&R, &G, &B, &A, mtt, mrr, muu);
    mtt->RR += mtt->TT*A*R;
    mtt->GG += mtt->TT*A*G;
    mtt->BB += mtt->TT*A*B;
    mtt->TT *= 1-A;
    /*
    if (mtt->verbose) {
      fprintf(stderr, "%s: after compositing: RGBT = %g,%g,%g,%g\n",
              me, mtt->RR, mtt->GG, mtt->BB, mtt->TT);
    }
    */
    /* fprintf(stderr, "%s: mtt->TT = %g\n", me, mtt->TT); */
  } else {
    R = G = B = A = 0;
  }
  if (mtt->verbose) {
    dbg = muu->debug + muu->debugIdx;
    dbg[0 + 2*mtt->stageNum] = R;
    dbg[1 + 2*mtt->stageNum] = G;
    dbg[2 + 2*mtt->stageNum] = B;
    dbg[3 + 2*mtt->stageNum] = A;
    dbg[4 + 2*mtt->stageNum] = rayT;
  }

  /* set Z if it hasn't been set already */
  if (1-mtt->TT >= muu->opacMatters && !AIR_EXISTS(mtt->ZZ)) {
    mtt->ZZ = rayT;
  }

  /* this is used to index mtt->debug */
  mtt->raySample += 1;

  return mtt->rayStep;
}