ed5f96a4541b34faaf4bd583292e1d74bdb124aa
[blender.git] / intern / elbeem / intern / ntl_ray.cpp
1 /******************************************************************************
2  *
3  * El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
4  * Copyright 2003,2004 Nils Thuerey
5  *
6  * main renderer class
7  *
8  *****************************************************************************/
9
10
11 #include "ntl_ray.h"
12 #include "ntl_scene.h"
13
14
15 /* Minimum value for refl/refr to be traced */
16 #define RAY_THRESHOLD 0.001
17
18 #if GFX_PRECISION==1
19 // float values
20 //! Minimal contribution for rays to be traced on
21 #define RAY_MINCONTRIB (1e-04)
22
23 #else 
24 // double values
25 //! Minimal contribution for rays to be traced on
26 #define RAY_MINCONTRIB (1e-05)
27
28 #endif 
29
30
31
32
33
34 /******************************************************************************
35  * Constructor
36  *****************************************************************************/
37 ntlRay::ntlRay( void )
38   : mOrigin(0.0)
39   , mDirection(0.0)
40   , mvNormal(0.0)
41   , mDepth(0)
42   , mpGlob(NULL)
43   , mIsRefracted(0)
44 {
45   errFatal("ntlRay::ntlRay()","Don't use uninitialized rays !", SIMWORLD_GENERICERROR);
46         return;
47 }
48
49
50 /******************************************************************************
51  * Copy - Constructor
52  *****************************************************************************/
53 ntlRay::ntlRay( const ntlRay &r )
54 {
55   // copy it! initialization is not enough!
56   mOrigin    = r.mOrigin;
57   mDirection = r.mDirection;
58   mvNormal   = r.mvNormal;
59   mDepth     = r.mDepth;
60   mIsRefracted  = r.mIsRefracted;
61   mIsReflected  = r.mIsReflected;
62         mContribution = r.mContribution;
63   mpGlob        = r.mpGlob;
64
65         // get new ID
66         if(mpGlob) {
67                 mID           = mpGlob->getCounterRays()+1;
68                 mpGlob->setCounterRays( mpGlob->getCounterRays()+1 );
69         } else {
70                 mID = 0;
71         }
72 }
73
74
75 /******************************************************************************
76  * Constructor with explicit parameters and global render object
77  *****************************************************************************/
78 ntlRay::ntlRay(const ntlVec3Gfx &o, const ntlVec3Gfx &d, unsigned int i, gfxReal contrib, ntlRenderGlobals *glob)
79   : mOrigin( o )
80   , mDirection( d )
81   , mvNormal(0.0)
82   , mDepth( i )
83         , mContribution( contrib )
84   , mpGlob( glob )
85   , mIsRefracted( 0 )
86         , mIsReflected( 0 )
87 {
88         // get new ID
89         if(mpGlob) {
90                 mID           = mpGlob->getCounterRays()+1;
91                 mpGlob->setCounterRays( mpGlob->getCounterRays()+1 );
92         } else {
93                 mID = 0;
94         }
95 }
96
97
98
99 /******************************************************************************
100  * Destructor
101  *****************************************************************************/
102 ntlRay::~ntlRay()
103 {
104   /* nothing to do... */
105 }
106
107
108
109 /******************************************************************************
110  * AABB
111  *****************************************************************************/
112 /* for AABB intersect */
113 #define NUMDIM 3
114 #define RIGHT  0
115 #define LEFT   1
116 #define MIDDLE 2
117
118 //! intersect ray with AABB
119 #ifndef ELBEEM_BLENDER
120 void ntlRay::intersectFrontAABB(ntlVec3Gfx mStart, ntlVec3Gfx mEnd, gfxReal &t, ntlVec3Gfx &retnormal,ntlVec3Gfx &retcoord) const
121 {
122   char   inside = true;   /* inside box? */
123   char   hit    = false;  /* ray hits box? */
124   int    whichPlane;      /* intersection plane */
125   gfxReal candPlane[NUMDIM];  /* candidate plane */
126   gfxReal quadrant[NUMDIM];   /* quadrants */
127   gfxReal maxT[NUMDIM];       /* max intersection T for planes */
128   ntlVec3Gfx  coord;           /* intersection point */
129   ntlVec3Gfx  dir = mDirection;
130   ntlVec3Gfx  origin = mOrigin;
131   ntlVec3Gfx  normal(0.0, 0.0, 0.0);
132
133   t = GFX_REAL_MAX;
134
135   /* check intersection planes for AABB */
136   for(int i=0;i<NUMDIM;i++) {
137     if(origin[i] < mStart[i]) {
138       quadrant[i] = LEFT;
139       candPlane [i] = mStart[i];
140       inside = false;
141     } else if(origin[i] > mEnd[i]) {
142       quadrant[i] = RIGHT;
143       candPlane[i] = mEnd[i];
144       inside = false;
145     } else {
146       quadrant[i] = MIDDLE;
147     }
148   }
149
150   /* inside AABB? */
151   if(!inside) {
152     /* get t distances to planes */
153     /* treat too small direction components as paralell */
154     for(int i=0;i<NUMDIM;i++) {
155       if((quadrant[i] != MIDDLE) && (fabs(dir[i]) > getVecEpsilon()) ) {
156                                 maxT[i] = (candPlane[i] - origin[i]) / dir[i];
157       } else {
158                                 maxT[i] = -1;
159       }
160     }
161     
162     /* largest max t */
163     whichPlane = 0;
164     for(int i=1;i<NUMDIM;i++) {
165       if(maxT[whichPlane] < maxT[i]) whichPlane = i;
166     }
167     
168     /* check final candidate */
169     hit  = true;
170     if(maxT[whichPlane] >= 0.0) {
171
172       for(int i=0;i<NUMDIM;i++) {
173                                 if(whichPlane != i) {
174                                         coord[i] = origin[i] + maxT[whichPlane] * dir[i];
175                                         if( (coord[i] < mStart[i]-getVecEpsilon() ) || 
176                                                         (coord[i] > mEnd[i]  +getVecEpsilon() )  ) {
177                                                 /* no hit... */
178                                                 hit  = false;
179                                         } 
180                                 }
181                                 else {
182                                         coord[i] = candPlane[i];
183                                 }      
184       }
185
186       /* AABB hit... */
187       if( hit ) {
188                                 t = maxT[whichPlane];   
189                                 if(quadrant[whichPlane]==RIGHT) normal[whichPlane] = 1.0;
190                                 else normal[whichPlane] = -1.0;
191       }
192     }
193     
194
195   } else {
196     /* inside AABB... */
197     t  = 0.0;
198     coord = origin;
199     return;
200   }
201
202   if(t == GFX_REAL_MAX) t = -1.0;
203   retnormal = normal;
204   retcoord = coord;
205 }
206
207 //! intersect ray with AABB
208 void ntlRay::intersectBackAABB(ntlVec3Gfx mStart, ntlVec3Gfx mEnd, gfxReal &t, ntlVec3Gfx &retnormal,ntlVec3Gfx &retcoord) const
209 {
210   char   hit    = false;  /* ray hits box? */
211   int    whichPlane;      /* intersection plane */
212   gfxReal candPlane[NUMDIM]; /* candidate plane */
213   gfxReal quadrant[NUMDIM];  /* quadrants */
214   gfxReal maxT[NUMDIM];    /* max intersection T for planes */
215   ntlVec3Gfx  coord;           /* intersection point */
216   ntlVec3Gfx  dir = mDirection;
217   ntlVec3Gfx  origin = mOrigin;
218   ntlVec3Gfx  normal(0.0, 0.0, 0.0);
219
220   t = GFX_REAL_MAX;
221   for(int i=0;i<NUMDIM;i++) {
222     if(origin[i] < mStart[i]) {
223       quadrant[i] = LEFT;
224       candPlane [i] = mEnd[i];
225     } else if(origin[i] > mEnd[i]) {
226       quadrant[i] = RIGHT;
227       candPlane[i] = mStart[i];
228     } else {
229       if(dir[i] > 0) {
230                                 quadrant[i] = LEFT;
231                                 candPlane [i] = mEnd[i];
232       } else 
233                                 if(dir[i] < 0) {
234                                         quadrant[i] = RIGHT;
235                                         candPlane[i] = mStart[i];
236                                 } else {
237                                         quadrant[i] = MIDDLE;
238                                 }
239     }
240   }
241
242
243         /* get t distances to planes */
244         /* treat too small direction components as paralell */
245         for(int i=0;i<NUMDIM;i++) {
246                 if((quadrant[i] != MIDDLE) && (fabs(dir[i]) > getVecEpsilon()) ) {
247                         maxT[i] = (candPlane[i] - origin[i]) / dir[i];
248                 } else {
249                         maxT[i] = GFX_REAL_MAX;
250                 }
251         }
252     
253         /* largest max t */
254         whichPlane = 0;
255         for(int i=1;i<NUMDIM;i++) {
256                 if(maxT[whichPlane] > maxT[i]) whichPlane = i;
257         }
258     
259         /* check final candidate */
260         hit  = true;
261         if(maxT[whichPlane] != GFX_REAL_MAX) {
262
263                 for(int i=0;i<NUMDIM;i++) {
264                         if(whichPlane != i) {
265                                 coord[i] = origin[i] + maxT[whichPlane] * dir[i];
266                                 if( (coord[i] < mStart[i]-getVecEpsilon() ) || 
267                                                 (coord[i] > mEnd[i]  +getVecEpsilon() )  ) {
268                                         /* no hit... */
269                                         hit  = false;
270                                 } 
271                         }
272                         else {
273                                 coord[i] = candPlane[i];
274                         }      
275                 }
276
277                 /* AABB hit... */
278                 if( hit ) {
279                         t = maxT[whichPlane];
280         
281                         if(quadrant[whichPlane]==RIGHT) normal[whichPlane] = 1.0;
282                         else normal[whichPlane] = -1.0;
283                 }
284         }
285     
286
287   if(t == GFX_REAL_MAX) t = -1.0;
288   retnormal = normal;
289   retcoord = coord;
290 }
291 #endif // ELBEEM_BLENDER
292
293 //! intersect ray with AABB
294 void ntlRay::intersectCompleteAABB(ntlVec3Gfx mStart, ntlVec3Gfx mEnd, gfxReal &tmin, gfxReal &tmax) const
295 {
296   char   inside = true;   /* inside box? */
297   char   hit    = false;  /* ray hits box? */
298   int    whichPlane;      /* intersection plane */
299   gfxReal candPlane[NUMDIM]; /* candidate plane */
300   gfxReal quadrant[NUMDIM];  /* quadrants */
301   gfxReal maxT[NUMDIM];    /* max intersection T for planes */
302   ntlVec3Gfx  coord;           /* intersection point */
303   ntlVec3Gfx  dir = mDirection;
304   ntlVec3Gfx  origin = mOrigin;
305   gfxReal t = GFX_REAL_MAX;
306
307   /* check intersection planes for AABB */
308   for(int i=0;i<NUMDIM;i++) {
309     if(origin[i] < mStart[i]) {
310       quadrant[i] = LEFT;
311       candPlane [i] = mStart[i];
312       inside = false;
313     } else if(origin[i] > mEnd[i]) {
314       quadrant[i] = RIGHT;
315       candPlane[i] = mEnd[i];
316       inside = false;
317     } else {
318       /* intersect with backside */
319       if(dir[i] > 0) {
320                                 quadrant[i] = LEFT;
321                                 candPlane [i] = mStart[i];
322       } else 
323                                 if(dir[i] < 0) {
324                                         quadrant[i] = RIGHT;
325                                         candPlane[i] = mEnd[i];
326                                 } else {
327                                         quadrant[i] = MIDDLE;
328                                 }
329     }
330   }
331
332   /* get t distances to planes */
333   for(int i=0;i<NUMDIM;i++) {
334     if((quadrant[i] != MIDDLE) && (fabs(dir[i]) > getVecEpsilon()) ) {
335       maxT[i] = (candPlane[i] - origin[i]) / dir[i];
336     } else {
337       maxT[i] = GFX_REAL_MAX;
338     }
339   }
340  
341   /* largest max t */
342   whichPlane = 0;
343   for(int i=1;i<NUMDIM;i++) {
344     if( ((maxT[whichPlane] < maxT[i])&&(maxT[i]!=GFX_REAL_MAX)) ||
345                                 (maxT[whichPlane]==GFX_REAL_MAX) )
346       whichPlane = i;
347   }
348     
349   /* check final candidate */
350   hit  = true;
351   if(maxT[whichPlane]<GFX_REAL_MAX) {
352     for(int i=0;i<NUMDIM;i++) {
353       if(whichPlane != i) {
354                                 coord[i] = origin[i] + maxT[whichPlane] * dir[i];
355                                 if( (coord[i] < mStart[i]-getVecEpsilon() ) || 
356                                                 (coord[i] > mEnd[i]  +getVecEpsilon() )  ) {
357                                         /* no hit... */
358                                         hit  = false;
359                                 } 
360       }
361       else { coord[i] = candPlane[i];   }      
362     }
363
364     /* AABB hit... */
365     if( hit ) {
366       t = maxT[whichPlane];     
367     }
368   }
369   tmin = t;
370
371   /* now the backside */
372   t = GFX_REAL_MAX;
373   for(int i=0;i<NUMDIM;i++) {
374     if(origin[i] < mStart[i]) {
375       quadrant[i] = LEFT;
376       candPlane [i] = mEnd[i];
377     } else if(origin[i] > mEnd[i]) {
378       quadrant[i] = RIGHT;
379       candPlane[i] = mStart[i];
380     } else {
381       if(dir[i] > 0) {
382                                 quadrant[i] = LEFT;
383                                 candPlane [i] = mEnd[i];
384       } else 
385                                 if(dir[i] < 0) {
386                                         quadrant[i] = RIGHT;
387                                         candPlane[i] = mStart[i];
388                                 } else {
389                                         quadrant[i] = MIDDLE;
390                                 }
391     }
392   }
393
394
395   /* get t distances to planes */
396   for(int i=0;i<NUMDIM;i++) {
397     if((quadrant[i] != MIDDLE) && (fabs(dir[i]) > getVecEpsilon()) ) {
398       maxT[i] = (candPlane[i] - origin[i]) / dir[i];
399     } else {
400       maxT[i] = GFX_REAL_MAX;
401     }
402   }
403     
404   /* smallest max t */
405   whichPlane = 0;
406   for(int i=1;i<NUMDIM;i++) {
407     if(maxT[whichPlane] > maxT[i]) whichPlane = i;
408   }
409     
410   /* check final candidate */
411   hit  = true;
412   if(maxT[whichPlane] != GFX_REAL_MAX) {
413
414     for(int i=0;i<NUMDIM;i++) {
415       if(whichPlane != i) {
416                                 coord[i] = origin[i] + maxT[whichPlane] * dir[i];
417                                 if( (coord[i] < mStart[i]-getVecEpsilon() ) || 
418                                                 (coord[i] > mEnd[i]  +getVecEpsilon() )  ) {
419                                         /* no hit... */
420                                         hit  = false;
421                                 } 
422       }
423       else {
424                                 coord[i] = candPlane[i];
425       }      
426     }
427
428     /* AABB hit... */
429     if( hit ) {
430       t = maxT[whichPlane];
431     }
432   }
433    
434   tmax = t;
435 }
436
437
438
439 /******************************************************************************
440  * Determine color of this ray by tracing through the scene
441  *****************************************************************************/
442 const ntlColor ntlRay::shade() //const
443 {
444 #ifndef ELBEEM_BLENDER
445   ntlGeometryObject           *closest = NULL;
446   gfxReal                      minT = GFX_REAL_MAX;
447   vector<ntlLightObject*>     *lightlist = mpGlob->getLightList();
448   mpGlob->setCounterShades( mpGlob->getCounterShades()+1 );
449         bool intersectionInside = 0;
450         if(mpGlob->getDebugOut() > 5) errorOut(std::endl<<"New Ray: depth "<<mDepth<<", org "<<mOrigin<<", dir "<<mDirection );
451
452         /* check if this ray contributes enough */
453         if(mContribution <= RAY_MINCONTRIB) {
454                 //return ntlColor(0.0);
455         }
456         
457   /* find closes object that intersects */
458         ntlTriangle *tri = NULL;
459         ntlVec3Gfx normal;
460         mpGlob->getScene()->intersectScene(*this, minT, normal, tri, 0);
461         if(minT>0) {
462                 closest = mpGlob->getScene()->getObject( tri->getObjectId() );
463         }
464
465   /* object hit... */
466   if (closest != NULL) {
467
468                 ntlVec3Gfx triangleNormal = tri->getNormal();
469                 if( equal(triangleNormal, ntlVec3Gfx(0.0)) ) errorOut("ntlRay warning: trinagle normal= 0 "); // DEBUG
470                 /* intersection on inside faces? if yes invert normal afterwards */
471                 gfxReal valDN; // = mDirection | normal;
472                 valDN = dot(mDirection, triangleNormal);
473                 if( valDN > 0.0) {
474                         intersectionInside = 1;
475                         normal = normal * -1.0;
476                         triangleNormal = triangleNormal * -1.0;
477                 } 
478
479     /* ... -> do reflection */
480     ntlVec3Gfx intersectionPosition(mOrigin + (mDirection * (minT)) );
481     ntlMaterial *clossurf = closest->getMaterial();
482                 /*if(mpGlob->getDebugOut() > 5) {
483                         errorOut("Ray hit: at "<<intersectionPosition<<" n:"<<normal<<"    dn:"<<valDN<<" ins:"<<intersectionInside<<"  cl:"<<((unsigned int)closest) ); 
484                         errorOut(" t1:"<<mpGlob->getScene()->getVertex(tri->getPoints()[0])<<" t2:"<<mpGlob->getScene()->getVertex(tri->getPoints()[1])<<" t3:"<<mpGlob->getScene()->getVertex(tri->getPoints()[2]) ); 
485                         errorOut(" trin:"<<tri->getNormal() );
486                 } // debug */
487
488                 /* current transparence and reflectivity */
489                 gfxReal currTrans = clossurf->getTransparence();
490                 gfxReal currRefl = clossurf->getMirror();
491
492     /* correct intersectopm position */
493     intersectionPosition += ( triangleNormal*getVecEpsilon() );
494                 /* reflection at normal */
495                 ntlVec3Gfx reflectedDir = getNormalized( reflectVector(mDirection, normal) );
496                 int badRefl = 0;
497                 if(dot(reflectedDir, triangleNormal)<0.0 ) {
498                         badRefl = 1;
499                         if(mpGlob->getDebugOut() > 5) { errorOut("Ray Bad reflection...!"); }
500                 }
501
502                 /* refraction direction, depending on in/outside hit */
503                 ntlVec3Gfx refractedDir;
504                 int refRefl = 0;
505                 /* refraction at normal is handled by inverting normal before */
506                 gfxReal myRefIndex = 1.0;
507                 if((currTrans>RAY_THRESHOLD)||(clossurf->getFresnel())) {
508                         if(intersectionInside) {
509                                 myRefIndex = 1.0/clossurf->getRefracIndex();
510                         } else {
511                                 myRefIndex = clossurf->getRefracIndex();
512                         }
513
514                         refractedDir = refractVector(mDirection, normal, myRefIndex , (gfxReal)(1.0) /* global ref index */, refRefl);
515                 }
516
517                 /* calculate fresnel? */
518                 if(clossurf->getFresnel()) {
519                         // for total reflection, just set trans to 0
520                         if(refRefl) {
521                                 currRefl = 1.0; currTrans = 0.0;
522                         } else {
523                                 // calculate fresnel coefficients
524                                 clossurf->calculateFresnel( mDirection, normal, myRefIndex, currRefl,currTrans );
525                         }
526                 }
527
528     ntlRay reflectedRay(intersectionPosition, reflectedDir, mDepth+1, mContribution*currRefl, mpGlob);
529                 reflectedRay.setNormal( normal );
530     ntlColor currentColor(0.0);
531     ntlColor highlightColor(0.0);
532
533     /* first add reflected ambient color */
534     currentColor += (clossurf->getAmbientRefl() * mpGlob->getAmbientLight() );
535
536     /* calculate lighting, not on the insides of objects... */
537                 if(!intersectionInside) {
538                         for (vector<ntlLightObject*>::iterator iter = lightlist->begin();
539                                          iter != lightlist->end();
540                                          iter++) {
541                                 
542                                 /* let light illuminate point */
543                                 currentColor += (*iter)->illuminatePoint( reflectedRay, closest, highlightColor );
544                                 
545                         } // for all lights
546                 }
547
548     // recurse ?
549     if ((mDepth < mpGlob->getRayMaxDepth() )&&(currRefl>RAY_THRESHOLD)) {
550
551                                 if(badRefl) {
552                                         ntlVec3Gfx intersectionPosition2;
553                                         ntlGeometryObject           *closest2 = NULL;
554                                         gfxReal                      minT2 = GFX_REAL_MAX;
555                                         ntlTriangle *tri2 = NULL;
556                                         ntlVec3Gfx normal2;
557
558                                         ntlVec3Gfx refractionPosition2(mOrigin + (mDirection * minT) );
559                                         refractionPosition2 -= (triangleNormal*getVecEpsilon() );
560
561                                         ntlRay reflectedRay2 = ntlRay(refractionPosition2, reflectedDir, mDepth+1, mContribution*currRefl, mpGlob);
562                                         mpGlob->getScene()->intersectScene(reflectedRay2, minT2, normal2, tri2, 0);
563                                         if(minT2>0) {
564                                                 closest2 = mpGlob->getScene()->getObject( tri2->getObjectId() );
565                                         }
566
567                                         /* object hit... */
568                                         if (closest2 != NULL) {
569                                                 ntlVec3Gfx triangleNormal2 = tri2->getNormal();
570                                                 gfxReal valDN2; 
571                                                 valDN2 = dot(reflectedDir, triangleNormal2);
572                                                 if( valDN2 > 0.0) {
573                                                         triangleNormal2 = triangleNormal2 * -1.0;
574                                                         intersectionPosition2 = ntlVec3Gfx(intersectionPosition + (reflectedDir * (minT2)) );
575                                                         /* correct intersection position and create new reflected ray */
576                                                         intersectionPosition2 += ( triangleNormal2*getVecEpsilon() );
577                                                         reflectedRay = ntlRay(intersectionPosition2, reflectedDir, mDepth+1, mContribution*currRefl, mpGlob);
578                                                 } else { 
579                                                         // ray seems to work, continue normally ?
580                                                 }
581
582                                         }
583
584                                 }
585
586       // add mirror color multiplied by mirror factor of surface
587                         if(mpGlob->getDebugOut() > 5) errorOut("Reflected ray from depth "<<mDepth<<", dir "<<reflectedDir );
588                         ntlColor reflectedColor = reflectedRay.shade() * currRefl;
589                         currentColor += reflectedColor;
590     }
591
592     /* Trace another ray on for transparent objects */
593     if(currTrans > RAY_THRESHOLD) {
594
595       //gfxReal refrac_dn = mDirection | normal;
596       /* position at the other side of the surface, along ray */
597       ntlVec3Gfx refraction_position(mOrigin + (mDirection * minT) );
598       refraction_position += (mDirection * getVecEpsilon());
599                         refraction_position -= (triangleNormal*getVecEpsilon() );
600                         
601       ntlColor refracCol(0.0);         /* refracted color */
602                         
603
604       /* trace refracted ray */
605       ntlRay transRay(refraction_position, refractedDir, mDepth+1, mContribution*currTrans, mpGlob);
606       transRay.setRefracted(1);
607                         transRay.setNormal( normal );
608       if(mDepth < mpGlob->getRayMaxDepth() ) {
609                                 if(!refRefl) {
610                                         if(mpGlob->getDebugOut() > 5) errorOut("Refracted ray from depth "<<mDepth<<", dir "<<refractedDir );
611                                         refracCol = transRay.shade();
612                                 } else { 
613                                         //we shouldnt reach this!
614                                         if(mpGlob->getDebugOut() > 5) errorOut("Fully reflected ray from depth "<<mDepth<<", dir "<<reflectedDir );
615                                         refracCol = reflectedRay.shade();
616                                 }
617       }
618
619       /* calculate color */
620       /* additive transparency "light amplification" */
621       ntlColor add_col = currentColor + refracCol * currTrans;
622       /* subtractive transparency, more realistic */
623       ntlColor sub_col = (refracCol * currTrans) + 
624         ( currentColor * (1.0-currTrans) );
625
626       /* mix additive and subtractive */
627                         add_col += sub_col;
628                         currentColor += (refracCol * currTrans);
629
630     }
631
632                 /* add highlights (should not be affected by transparence as the diffuse reflections */
633                 currentColor += highlightColor;
634
635                 /* attentuate as a last step*/
636                 /* check if we're on the inside or outside */
637                 if(intersectionInside) {
638                         gfxReal kr,kg,kb;    /* attentuation */
639                         /* calculate attentuation */
640                         ntlColor attCol = clossurf->getTransAttCol();
641                         kr = exp( attCol[0] * minT );
642                         kg = exp( attCol[1] * minT );
643                         kb = exp( attCol[2] * minT );
644                         currentColor = currentColor * ntlColor(kr,kg,kb);
645                 }
646
647     /* done... */
648                 if(mpGlob->getDebugOut() > 5) { errorOut("Ray "<<mDepth<<" color "<<currentColor ); }
649     return ntlColor(currentColor);
650   }
651
652 #endif // ELBEEM_BLENDER
653   /* no object hit -> ray goes to infinity */
654   return mpGlob->getBackgroundCol(); 
655 }
656
657
658
659
660
661