Cleanup: remove redundant doxygen \file argument
[blender.git] / intern / elbeem / intern / ntl_ray.cpp
1 /** \file \ingroup elbeem
2  */
3 /******************************************************************************
4  *
5  * El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
6  * Copyright 2003-2006 Nils Thuerey
7  *
8  * main renderer class
9  *
10  *****************************************************************************/
11
12
13 #include "utilities.h"
14 #include "ntl_ray.h"
15 #include "ntl_world.h"
16 #include "ntl_geometryobject.h"
17 #include "ntl_geometryshader.h"
18
19
20 /* Minimum value for refl/refr to be traced */
21 #define RAY_THRESHOLD 0.001
22
23 #if GFX_PRECISION==1
24 // float values
25 //! Minimal contribution for rays to be traced on
26 #define RAY_MINCONTRIB (1e-04)
27
28 #else 
29 // double values
30 //! Minimal contribution for rays to be traced on
31 #define RAY_MINCONTRIB (1e-05)
32
33 #endif 
34
35
36
37
38
39 /******************************************************************************
40  * Constructor
41  *****************************************************************************/
42 ntlRay::ntlRay( void )
43   : mOrigin(0.0)
44   , mDirection(0.0)
45   , mvNormal(0.0)
46   , mDepth(0)
47   , mpGlob(NULL)
48   , mIsRefracted(0)
49 {
50   errFatal("ntlRay::ntlRay()","Don't use uninitialized rays !", SIMWORLD_GENERICERROR);
51         return;
52 }
53
54
55 /******************************************************************************
56  * Copy - Constructor
57  *****************************************************************************/
58 ntlRay::ntlRay( const ntlRay &r )
59 {
60   // copy it! initialization is not enough!
61   mOrigin    = r.mOrigin;
62   mDirection = r.mDirection;
63   mvNormal   = r.mvNormal;
64   mDepth     = r.mDepth;
65   mIsRefracted  = r.mIsRefracted;
66   mIsReflected  = r.mIsReflected;
67         mContribution = r.mContribution;
68   mpGlob        = r.mpGlob;
69
70         // get new ID
71         if(mpGlob) {
72                 mID           = mpGlob->getCounterRays()+1;
73                 mpGlob->setCounterRays( mpGlob->getCounterRays()+1 );
74         } else {
75                 mID = 0;
76         }
77 }
78
79
80 /******************************************************************************
81  * Constructor with explicit parameters and global render object
82  *****************************************************************************/
83 ntlRay::ntlRay(const ntlVec3Gfx &o, const ntlVec3Gfx &d, unsigned int i, gfxReal contrib, ntlRenderGlobals *glob)
84   : mOrigin( o )
85   , mDirection( d )
86   , mvNormal(0.0)
87   , mDepth( i )
88         , mContribution( contrib )
89   , mpGlob( glob )
90   , mIsRefracted( 0 )
91         , mIsReflected( 0 )
92 {
93         // get new ID
94         if(mpGlob) {
95                 mID           = mpGlob->getCounterRays()+1;
96                 mpGlob->setCounterRays( mpGlob->getCounterRays()+1 );
97         } else {
98                 mID = 0;
99         }
100 }
101
102
103
104 /******************************************************************************
105  * Destructor
106  *****************************************************************************/
107 ntlRay::~ntlRay()
108 {
109   /* nothing to do... */
110 }
111
112
113
114 /******************************************************************************
115  * AABB
116  *****************************************************************************/
117 /* for AABB intersect */
118 #define NUMDIM 3
119 #define RIGHT  0
120 #define LEFT   1
121 #define MIDDLE 2
122
123 //! intersect ray with AABB
124 #ifndef ELBEEM_PLUGIN
125 void ntlRay::intersectFrontAABB(ntlVec3Gfx mStart, ntlVec3Gfx mEnd, gfxReal &t, ntlVec3Gfx &retnormal,ntlVec3Gfx &retcoord) const
126 {
127   char   inside = true;   /* inside box? */
128   char   hit    = false;  /* ray hits box? */
129   int    whichPlane;      /* intersection plane */
130   gfxReal candPlane[NUMDIM];  /* candidate plane */
131   gfxReal quadrant[NUMDIM];   /* quadrants */
132   gfxReal maxT[NUMDIM];       /* max intersection T for planes */
133   ntlVec3Gfx  coord;           /* intersection point */
134   ntlVec3Gfx  dir = mDirection;
135   ntlVec3Gfx  origin = mOrigin;
136   ntlVec3Gfx  normal(0.0, 0.0, 0.0);
137
138   t = GFX_REAL_MAX;
139
140   /* check intersection planes for AABB */
141   for(int i=0;i<NUMDIM;i++) {
142     if(origin[i] < mStart[i]) {
143       quadrant[i] = LEFT;
144       candPlane [i] = mStart[i];
145       inside = false;
146     } else if(origin[i] > mEnd[i]) {
147       quadrant[i] = RIGHT;
148       candPlane[i] = mEnd[i];
149       inside = false;
150     } else {
151       quadrant[i] = MIDDLE;
152     }
153   }
154
155   /* inside AABB? */
156   if(!inside) {
157     /* get t distances to planes */
158     /* treat too small direction components as paralell */
159     for(int i=0;i<NUMDIM;i++) {
160       if((quadrant[i] != MIDDLE) && (fabs(dir[i]) > getVecEpsilon()) ) {
161                                 maxT[i] = (candPlane[i] - origin[i]) / dir[i];
162       } else {
163                                 maxT[i] = -1;
164       }
165     }
166     
167     /* largest max t */
168     whichPlane = 0;
169     for(int i=1;i<NUMDIM;i++) {
170       if(maxT[whichPlane] < maxT[i]) whichPlane = i;
171     }
172     
173     /* check final candidate */
174     hit  = true;
175     if(maxT[whichPlane] >= 0.0) {
176
177       for(int i=0;i<NUMDIM;i++) {
178                                 if(whichPlane != i) {
179                                         coord[i] = origin[i] + maxT[whichPlane] * dir[i];
180                                         if( (coord[i] < mStart[i]-getVecEpsilon() ) || 
181                                                         (coord[i] > mEnd[i]  +getVecEpsilon() )  ) {
182                                                 /* no hit... */
183                                                 hit  = false;
184                                         } 
185                                 }
186                                 else {
187                                         coord[i] = candPlane[i];
188                                 }      
189       }
190
191       /* AABB hit... */
192       if( hit ) {
193                                 t = maxT[whichPlane];   
194                                 if(quadrant[whichPlane]==RIGHT) normal[whichPlane] = 1.0;
195                                 else normal[whichPlane] = -1.0;
196       }
197     }
198     
199
200   } else {
201     /* inside AABB... */
202     t  = 0.0;
203     coord = origin;
204     return;
205   }
206
207   if(t == GFX_REAL_MAX) t = -1.0;
208   retnormal = normal;
209   retcoord = coord;
210 }
211
212 //! intersect ray with AABB
213 void ntlRay::intersectBackAABB(ntlVec3Gfx mStart, ntlVec3Gfx mEnd, gfxReal &t, ntlVec3Gfx &retnormal,ntlVec3Gfx &retcoord) const
214 {
215   char   hit    = false;  /* ray hits box? */
216   int    whichPlane;      /* intersection plane */
217   gfxReal candPlane[NUMDIM]; /* candidate plane */
218   gfxReal quadrant[NUMDIM];  /* quadrants */
219   gfxReal maxT[NUMDIM];    /* max intersection T for planes */
220   ntlVec3Gfx  coord;           /* intersection point */
221   ntlVec3Gfx  dir = mDirection;
222   ntlVec3Gfx  origin = mOrigin;
223   ntlVec3Gfx  normal(0.0, 0.0, 0.0);
224
225   t = GFX_REAL_MAX;
226   for(int i=0;i<NUMDIM;i++) {
227     if(origin[i] < mStart[i]) {
228       quadrant[i] = LEFT;
229       candPlane [i] = mEnd[i];
230     } else if(origin[i] > mEnd[i]) {
231       quadrant[i] = RIGHT;
232       candPlane[i] = mStart[i];
233     } else {
234       if(dir[i] > 0) {
235                                 quadrant[i] = LEFT;
236                                 candPlane [i] = mEnd[i];
237       } else 
238                                 if(dir[i] < 0) {
239                                         quadrant[i] = RIGHT;
240                                         candPlane[i] = mStart[i];
241                                 } else {
242                                         quadrant[i] = MIDDLE;
243                                 }
244     }
245   }
246
247
248         /* get t distances to planes */
249         /* treat too small direction components as paralell */
250         for(int i=0;i<NUMDIM;i++) {
251                 if((quadrant[i] != MIDDLE) && (fabs(dir[i]) > getVecEpsilon()) ) {
252                         maxT[i] = (candPlane[i] - origin[i]) / dir[i];
253                 } else {
254                         maxT[i] = GFX_REAL_MAX;
255                 }
256         }
257     
258         /* largest max t */
259         whichPlane = 0;
260         for(int i=1;i<NUMDIM;i++) {
261                 if(maxT[whichPlane] > maxT[i]) whichPlane = i;
262         }
263     
264         /* check final candidate */
265         hit  = true;
266         if(maxT[whichPlane] != GFX_REAL_MAX) {
267
268                 for(int i=0;i<NUMDIM;i++) {
269                         if(whichPlane != i) {
270                                 coord[i] = origin[i] + maxT[whichPlane] * dir[i];
271                                 if( (coord[i] < mStart[i]-getVecEpsilon() ) || 
272                                                 (coord[i] > mEnd[i]  +getVecEpsilon() )  ) {
273                                         /* no hit... */
274                                         hit  = false;
275                                 } 
276                         }
277                         else {
278                                 coord[i] = candPlane[i];
279                         }      
280                 }
281
282                 /* AABB hit... */
283                 if( hit ) {
284                         t = maxT[whichPlane];
285         
286                         if(quadrant[whichPlane]==RIGHT) normal[whichPlane] = 1.0;
287                         else normal[whichPlane] = -1.0;
288                 }
289         }
290     
291
292   if(t == GFX_REAL_MAX) t = -1.0;
293   retnormal = normal;
294   retcoord = coord;
295 }
296 #endif // ELBEEM_PLUGIN
297
298 //! intersect ray with AABB
299 void ntlRay::intersectCompleteAABB(ntlVec3Gfx mStart, ntlVec3Gfx mEnd, gfxReal &tmin, gfxReal &tmax) const
300 {
301   // char   inside = true;   /* inside box? */  /* UNUSED */
302   char   hit    = false;  /* ray hits box? */
303   int    whichPlane;      /* intersection plane */
304   gfxReal candPlane[NUMDIM]; /* candidate plane */
305   gfxReal quadrant[NUMDIM];  /* quadrants */
306   gfxReal maxT[NUMDIM];    /* max intersection T for planes */
307   ntlVec3Gfx  coord;           /* intersection point */
308   ntlVec3Gfx  dir = mDirection;
309   ntlVec3Gfx  origin = mOrigin;
310   gfxReal t = GFX_REAL_MAX;
311
312   /* check intersection planes for AABB */
313   for(int i=0;i<NUMDIM;i++) {
314     if(origin[i] < mStart[i]) {
315       quadrant[i] = LEFT;
316       candPlane [i] = mStart[i];
317       // inside = false;
318     } else if(origin[i] > mEnd[i]) {
319       quadrant[i] = RIGHT;
320       candPlane[i] = mEnd[i];
321       // inside = false;
322     } else {
323       /* intersect with backside */
324       if(dir[i] > 0) {
325                                 quadrant[i] = LEFT;
326                                 candPlane [i] = mStart[i];
327       } else 
328                                 if(dir[i] < 0) {
329                                         quadrant[i] = RIGHT;
330                                         candPlane[i] = mEnd[i];
331                                 } else {
332                                         quadrant[i] = MIDDLE;
333                                 }
334     }
335   }
336
337   /* get t distances to planes */
338   for(int i=0;i<NUMDIM;i++) {
339     if((quadrant[i] != MIDDLE) && (fabs(dir[i]) > getVecEpsilon()) ) {
340       maxT[i] = (candPlane[i] - origin[i]) / dir[i];
341     } else {
342       maxT[i] = GFX_REAL_MAX;
343     }
344   }
345  
346   /* largest max t */
347   whichPlane = 0;
348   for(int i=1;i<NUMDIM;i++) {
349     if( ((maxT[whichPlane] < maxT[i])&&(maxT[i]!=GFX_REAL_MAX)) ||
350                                 (maxT[whichPlane]==GFX_REAL_MAX) )
351       whichPlane = i;
352   }
353     
354   /* check final candidate */
355   hit  = true;
356   if(maxT[whichPlane]<GFX_REAL_MAX) {
357     for(int i=0;i<NUMDIM;i++) {
358       if(whichPlane != i) {
359                                 coord[i] = origin[i] + maxT[whichPlane] * dir[i];
360                                 if( (coord[i] < mStart[i]-getVecEpsilon() ) || 
361                                                 (coord[i] > mEnd[i]  +getVecEpsilon() )  ) {
362                                         /* no hit... */
363                                         hit  = false;
364                                 } 
365       }
366       else { coord[i] = candPlane[i];   }      
367     }
368
369     /* AABB hit... */
370     if( hit ) {
371       t = maxT[whichPlane];     
372     }
373   }
374   tmin = t;
375
376   /* now the backside */
377   t = GFX_REAL_MAX;
378   for(int i=0;i<NUMDIM;i++) {
379     if(origin[i] < mStart[i]) {
380       quadrant[i] = LEFT;
381       candPlane [i] = mEnd[i];
382     } else if(origin[i] > mEnd[i]) {
383       quadrant[i] = RIGHT;
384       candPlane[i] = mStart[i];
385     } else {
386       if(dir[i] > 0) {
387                                 quadrant[i] = LEFT;
388                                 candPlane [i] = mEnd[i];
389       } else 
390                                 if(dir[i] < 0) {
391                                         quadrant[i] = RIGHT;
392                                         candPlane[i] = mStart[i];
393                                 } else {
394                                         quadrant[i] = MIDDLE;
395                                 }
396     }
397   }
398
399
400   /* get t distances to planes */
401   for(int i=0;i<NUMDIM;i++) {
402     if((quadrant[i] != MIDDLE) && (fabs(dir[i]) > getVecEpsilon()) ) {
403       maxT[i] = (candPlane[i] - origin[i]) / dir[i];
404     } else {
405       maxT[i] = GFX_REAL_MAX;
406     }
407   }
408     
409   /* smallest max t */
410   whichPlane = 0;
411   for(int i=1;i<NUMDIM;i++) {
412     if(maxT[whichPlane] > maxT[i]) whichPlane = i;
413   }
414     
415   /* check final candidate */
416   hit  = true;
417   if(maxT[whichPlane] != GFX_REAL_MAX) {
418
419     for(int i=0;i<NUMDIM;i++) {
420       if(whichPlane != i) {
421                                 coord[i] = origin[i] + maxT[whichPlane] * dir[i];
422                                 if( (coord[i] < mStart[i]-getVecEpsilon() ) || 
423                                                 (coord[i] > mEnd[i]  +getVecEpsilon() )  ) {
424                                         /* no hit... */
425                                         hit  = false;
426                                 } 
427       }
428       else {
429                                 coord[i] = candPlane[i];
430       }      
431     }
432
433     /* AABB hit... */
434     if( hit ) {
435       t = maxT[whichPlane];
436     }
437   }
438    
439   tmax = t;
440 }
441
442
443
444 /******************************************************************************
445  * Determine color of this ray by tracing through the scene
446  *****************************************************************************/
447 const ntlColor ntlRay::shade() //const
448 {
449 #ifndef ELBEEM_PLUGIN
450   ntlGeometryObject           *closest = NULL;
451   gfxReal                      minT = GFX_REAL_MAX;
452   vector<ntlLightObject*>     *lightlist = mpGlob->getLightList();
453   mpGlob->setCounterShades( mpGlob->getCounterShades()+1 );
454         bool intersectionInside = 0;
455         if(mpGlob->getDebugOut() > 5) errorOut(std::endl<<"New Ray: depth "<<mDepth<<", org "<<mOrigin<<", dir "<<mDirection );
456
457         /* check if this ray contributes enough */
458         if(mContribution <= RAY_MINCONTRIB) {
459                 //return ntlColor(0.0);
460         }
461         
462   /* find closes object that intersects */
463         ntlTriangle *tri = NULL;
464         ntlVec3Gfx normal;
465         mpGlob->getRenderScene()->intersectScene(*this, minT, normal, tri, 0);
466         if(minT>0) {
467                 closest = mpGlob->getRenderScene()->getObject( tri->getObjectId() );
468         }
469
470   /* object hit... */
471   if (closest != NULL) {
472
473                 ntlVec3Gfx triangleNormal = tri->getNormal();
474                 if( equal(triangleNormal, ntlVec3Gfx(0.0)) ) errorOut("ntlRay warning: trinagle normal= 0 "); // DEBUG
475                 /* intersection on inside faces? if yes invert normal afterwards */
476                 gfxReal valDN; // = mDirection | normal;
477                 valDN = dot(mDirection, triangleNormal);
478                 if( valDN > 0.0) {
479                         intersectionInside = 1;
480                         normal = normal * -1.0;
481                         triangleNormal = triangleNormal * -1.0;
482                 } 
483
484     /* ... -> do reflection */
485     ntlVec3Gfx intersectionPosition(mOrigin + (mDirection * (minT)) );
486     ntlMaterial *clossurf = closest->getMaterial();
487                 /*if(mpGlob->getDebugOut() > 5) {
488                         errorOut("Ray hit: at "<<intersectionPosition<<" n:"<<normal<<"    dn:"<<valDN<<" ins:"<<intersectionInside<<"  cl:"<<((unsigned int)closest) ); 
489                         errorOut(" t1:"<<mpGlob->getRenderScene()->getVertex(tri->getPoints()[0])<<" t2:"<<mpGlob->getRenderScene()->getVertex(tri->getPoints()[1])<<" t3:"<<mpGlob->getScene()->getVertex(tri->getPoints()[2]) ); 
490                         errorOut(" trin:"<<tri->getNormal() );
491                 } // debug */
492
493                 /* current transparence and reflectivity */
494                 gfxReal currTrans = clossurf->getTransparence();
495                 gfxReal currRefl = clossurf->getMirror();
496
497     /* correct intersectopm position */
498     intersectionPosition += ( triangleNormal*getVecEpsilon() );
499                 /* reflection at normal */
500                 ntlVec3Gfx reflectedDir = getNormalized( reflectVector(mDirection, normal) );
501                 int badRefl = 0;
502                 if(dot(reflectedDir, triangleNormal)<0.0 ) {
503                         badRefl = 1;
504                         if(mpGlob->getDebugOut() > 5) { errorOut("Ray Bad reflection...!"); }
505                 }
506
507                 /* refraction direction, depending on in/outside hit */
508                 ntlVec3Gfx refractedDir;
509                 int refRefl = 0;
510                 /* refraction at normal is handled by inverting normal before */
511                 gfxReal myRefIndex = 1.0;
512                 if((currTrans>RAY_THRESHOLD)||(clossurf->getFresnel())) {
513                         if(intersectionInside) {
514                                 myRefIndex = 1.0/clossurf->getRefracIndex();
515                         } else {
516                                 myRefIndex = clossurf->getRefracIndex();
517                         }
518
519                         refractedDir = refractVector(mDirection, normal, myRefIndex , (gfxReal)(1.0) /* global ref index */, refRefl);
520                 }
521
522                 /* calculate fresnel? */
523                 if(clossurf->getFresnel()) {
524                         // for total reflection, just set trans to 0
525                         if(refRefl) {
526                                 currRefl = 1.0; currTrans = 0.0;
527                         } else {
528                                 // calculate fresnel coefficients
529                                 clossurf->calculateFresnel( mDirection, normal, myRefIndex, currRefl,currTrans );
530                         }
531                 }
532
533     ntlRay reflectedRay(intersectionPosition, reflectedDir, mDepth+1, mContribution*currRefl, mpGlob);
534                 reflectedRay.setNormal( normal );
535     ntlColor currentColor(0.0);
536     ntlColor highlightColor(0.0);
537
538     /* first add reflected ambient color */
539     currentColor += (clossurf->getAmbientRefl() * mpGlob->getAmbientLight() );
540
541     /* calculate lighting, not on the insides of objects... */
542                 if(!intersectionInside) {
543                         for (vector<ntlLightObject*>::iterator iter = lightlist->begin();
544                                          iter != lightlist->end();
545                                          iter++) {
546                                 
547                                 /* let light illuminate point */
548                                 currentColor += (*iter)->illuminatePoint( reflectedRay, closest, highlightColor );
549                                 
550                         } // for all lights
551                 }
552
553     // recurse ?
554     if ((mDepth < mpGlob->getRayMaxDepth() )&&(currRefl>RAY_THRESHOLD)) {
555
556                                 if(badRefl) {
557                                         ntlVec3Gfx intersectionPosition2;
558                                         ntlGeometryObject           *closest2 = NULL;
559                                         gfxReal                      minT2 = GFX_REAL_MAX;
560                                         ntlTriangle *tri2 = NULL;
561                                         ntlVec3Gfx normal2;
562
563                                         ntlVec3Gfx refractionPosition2(mOrigin + (mDirection * minT) );
564                                         refractionPosition2 -= (triangleNormal*getVecEpsilon() );
565
566                                         ntlRay reflectedRay2 = ntlRay(refractionPosition2, reflectedDir, mDepth+1, mContribution*currRefl, mpGlob);
567                                         mpGlob->getRenderScene()->intersectScene(reflectedRay2, minT2, normal2, tri2, 0);
568                                         if(minT2>0) {
569                                                 closest2 = mpGlob->getRenderScene()->getObject( tri2->getObjectId() );
570                                         }
571
572                                         /* object hit... */
573                                         if (closest2 != NULL) {
574                                                 ntlVec3Gfx triangleNormal2 = tri2->getNormal();
575                                                 gfxReal valDN2; 
576                                                 valDN2 = dot(reflectedDir, triangleNormal2);
577                                                 if( valDN2 > 0.0) {
578                                                         triangleNormal2 = triangleNormal2 * -1.0;
579                                                         intersectionPosition2 = ntlVec3Gfx(intersectionPosition + (reflectedDir * (minT2)) );
580                                                         /* correct intersection position and create new reflected ray */
581                                                         intersectionPosition2 += ( triangleNormal2*getVecEpsilon() );
582                                                         reflectedRay = ntlRay(intersectionPosition2, reflectedDir, mDepth+1, mContribution*currRefl, mpGlob);
583                                                 } else { 
584                                                         // ray seems to work, continue normally ?
585                                                 }
586
587                                         }
588
589                                 }
590
591       // add mirror color multiplied by mirror factor of surface
592                         if(mpGlob->getDebugOut() > 5) errorOut("Reflected ray from depth "<<mDepth<<", dir "<<reflectedDir );
593                         ntlColor reflectedColor = reflectedRay.shade() * currRefl;
594                         currentColor += reflectedColor;
595     }
596
597     /* Trace another ray on for transparent objects */
598     if(currTrans > RAY_THRESHOLD) {
599       /* position at the other side of the surface, along ray */
600       ntlVec3Gfx refraction_position(mOrigin + (mDirection * minT) );
601       refraction_position += (mDirection * getVecEpsilon());
602                         refraction_position -= (triangleNormal*getVecEpsilon() );
603       ntlColor refracCol(0.0);         /* refracted color */
604
605       /* trace refracted ray */
606       ntlRay transRay(refraction_position, refractedDir, mDepth+1, mContribution*currTrans, mpGlob);
607       transRay.setRefracted(1);
608                         transRay.setNormal( normal );
609       if(mDepth < mpGlob->getRayMaxDepth() ) {
610                                 // full reflection should make sure refracindex&fresnel are on...
611                                 if((0)||(!refRefl)) {
612                                         if(mpGlob->getDebugOut() > 5) errorOut("Refracted ray from depth "<<mDepth<<", dir "<<refractedDir );
613                                         refracCol = transRay.shade();
614                                 } else { 
615                                         //we shouldnt reach this!
616                                         if(mpGlob->getDebugOut() > 5) errorOut("Fully reflected ray from depth "<<mDepth<<", dir "<<reflectedDir );
617                                         refracCol = reflectedRay.shade();
618                                 }
619       }
620                         //errMsg("REFMIR","t"<<currTrans<<" thres"<<RAY_THRESHOLD<<" mirr"<<currRefl<<" refRefl"<<refRefl<<" md"<<mDepth);
621
622       /* calculate color */
623                         // use transadditive setting!?
624       /* additive transparency "light amplification" */
625       //? ntlColor add_col = currentColor + refracCol * currTrans;
626       /* mix additive and subtractive */
627                         //? add_col += sub_col;
628                         //? currentColor += (refracCol * currTrans);
629
630       /* subtractive transparency, more realistic */
631       ntlColor sub_col = (refracCol * currTrans) + ( currentColor * (1.0-currTrans) );
632                         currentColor = sub_col;
633
634     }
635
636                 /* add highlights (should not be affected by transparence as the diffuse reflections */
637                 currentColor += highlightColor;
638
639                 /* attentuate as a last step*/
640                 /* check if we're on the inside or outside */
641                 if(intersectionInside) {
642                         gfxReal kr,kg,kb;    /* attentuation */
643                         /* calculate attentuation */
644                         ntlColor attCol = clossurf->getTransAttCol();
645                         kr = exp( attCol[0] * minT );
646                         kg = exp( attCol[1] * minT );
647                         kb = exp( attCol[2] * minT );
648                         currentColor = currentColor * ntlColor(kr,kg,kb);
649                 }
650
651     /* done... */
652                 if(mpGlob->getDebugOut() > 5) { errorOut("Ray "<<mDepth<<" color "<<currentColor ); }
653     return ntlColor(currentColor);
654   }
655
656 #endif // ELBEEM_PLUGIN
657   /* no object hit -> ray goes to infinity */
658   return mpGlob->getBackgroundCol(); 
659 }
660
661
662
663 /******************************************************************************
664  ******************************************************************************
665  ******************************************************************************
666  * scene implementation
667  ******************************************************************************
668  ******************************************************************************
669  *****************************************************************************/
670
671
672
673 /******************************************************************************
674  * Constructor
675  *****************************************************************************/
676 ntlScene::ntlScene( ntlRenderGlobals *glob, bool del ) :
677         mpGlob( glob ), mSceneDel(del),
678         mpTree( NULL ),
679         mSceneBuilt( false ), mFirstInitDone( false )
680 {
681 }
682
683
684 /******************************************************************************
685  * Destructor
686  *****************************************************************************/
687 ntlScene::~ntlScene()
688 {
689         if(mpTree != NULL) delete mpTree;
690
691         // cleanup lists, only if this is the rendering cleanup scene
692         if(mSceneDel) 
693         {
694                 for (vector<ntlGeometryClass*>::iterator iter = mGeos.begin();
695                                 iter != mGeos.end(); iter++) {
696                         //errMsg("ntlScene::~ntlScene","Deleting obj "<<(*iter)->getName() );
697                         delete (*iter);
698                 }
699                 for (vector<ntlLightObject*>::iterator iter = mpGlob->getLightList()->begin();
700                                  iter != mpGlob->getLightList()->end(); iter++) {
701                         delete (*iter);
702                 }
703                 for (vector<ntlMaterial*>::iterator iter = mpGlob->getMaterials()->begin();
704                                  iter != mpGlob->getMaterials()->end(); iter++) {
705                         delete (*iter);
706                 }
707         }
708         errMsg("ntlScene::~ntlScene","Deleted, ObjFree:"<<mSceneDel);
709 }
710
711
712 /******************************************************************************
713  * Build the scene arrays (obj, tris etc.)
714  *****************************************************************************/
715 void ntlScene::buildScene(double time,bool firstInit)
716 {
717         const bool buildInfo=true;
718
719         if(firstInit) {
720                 mObjects.clear();
721                 /* init geometry array, first all standard objects */
722                 for (vector<ntlGeometryClass*>::iterator iter = mGeos.begin();
723                                 iter != mGeos.end(); iter++) {
724                         bool geoinit = false;
725                         int tid = (*iter)->getTypeId();
726                         if(tid & GEOCLASSTID_OBJECT) {
727                                 ntlGeometryObject *geoobj = (ntlGeometryObject*)(*iter);
728                                 geoinit = true;
729                                 mObjects.push_back( geoobj );
730                                 if(buildInfo) debMsgStd("ntlScene::BuildScene",DM_MSG,"added GeoObj "<<geoobj->getName()<<" Id:"<<geoobj->getObjectId(), 5 );
731                         }
732                         //if(geoshad) {
733                         if(tid & GEOCLASSTID_SHADER) {
734                                 ntlGeometryShader *geoshad = (ntlGeometryShader*)(*iter);
735                                 geoinit = true;
736                                 if(!mFirstInitDone) {
737                                         // only on first init
738                                         geoshad->initializeShader();
739                                 }
740                                 for (vector<ntlGeometryObject*>::iterator siter = geoshad->getObjectsBegin();
741                                                 siter != geoshad->getObjectsEnd();
742                                                 siter++) {
743                                         if(buildInfo) debMsgStd("ntlScene::BuildScene",DM_MSG,"added shader geometry "<<(*siter)->getName()<<" Id:"<<(*siter)->getObjectId(), 5 );
744                                         mObjects.push_back( (*siter) );
745                                 }
746                         }
747
748                         if(!geoinit) {
749                                 errFatal("ntlScene::BuildScene","Invalid geometry class!", SIMWORLD_INITERROR);
750                                 return;
751                         }
752                 }
753         }
754
755         // collect triangles
756         mTriangles.clear();
757         mVertices.clear();
758         mVertNormals.clear();
759
760         /* for test mode deactivate transparencies etc. */
761         if( mpGlob->getTestMode() ) {
762                 debugOut("ntlScene::buildScene : Test Mode activated!", 2);
763                 // assign random colors to dark materials
764                 int matCounter = 0;
765                 ntlColor stdCols[] = { ntlColor(0,0,1.0), ntlColor(0,1.0,0), ntlColor(1.0,0.7,0) , ntlColor(0.7,0,0.6) };
766                 int stdColNum = 4;
767                 for (vector<ntlMaterial*>::iterator iter = mpGlob->getMaterials()->begin();
768                                          iter != mpGlob->getMaterials()->end(); iter++) {
769                         (*iter)->setTransparence(0.0);
770                         (*iter)->setMirror(0.0);
771                         (*iter)->setFresnel(false);
772                         // too dark?
773                         if( norm((*iter)->getDiffuseRefl()) <0.01) {
774                                 (*iter)->setDiffuseRefl( stdCols[matCounter] );
775                                 matCounter ++;
776                                 matCounter = matCounter%stdColNum;
777                         }
778                 }
779
780                 // restrict output file size to 400
781                 float downscale = 1.0;
782                 if(mpGlob->getResX() > 400){ downscale = 400.0/(float)mpGlob->getResX(); }
783                 if(mpGlob->getResY() > 400){ 
784                         float downscale2 = 400.0/(float)mpGlob->getResY(); 
785                         if(downscale2<downscale) downscale=downscale2;
786                 }
787                 mpGlob->setResX( (int)(mpGlob->getResX() * downscale) );
788                 mpGlob->setResY( (int)(mpGlob->getResY() * downscale) );
789
790         }
791
792         /* collect triangles from objects */
793         int idCnt = 0;          // give IDs to objects
794         bool debugTriCollect = false;
795         if(debugTriCollect) debMsgStd("ntlScene::buildScene",DM_MSG,"Start...",5);
796   for (vector<ntlGeometryObject*>::iterator iter = mObjects.begin();
797        iter != mObjects.end();
798        iter++) {
799                 /* only add visible objects */
800                 if(firstInit) {
801                         if(debugTriCollect) debMsgStd("ntlScene::buildScene",DM_MSG,"Collect init of "<<(*iter)->getName()<<" idCnt:"<<idCnt, 4 );
802                         (*iter)->initialize( mpGlob ); }
803                 if(debugTriCollect) debMsgStd("ntlScene::buildScene",DM_MSG,"Collecting tris from "<<(*iter)->getName(), 4 );
804
805                 int vstart = mVertNormals.size();
806                 (*iter)->setObjectId(idCnt);
807                 (*iter)->getTriangles(time, &mTriangles, &mVertices, &mVertNormals, idCnt);
808                 (*iter)->applyTransformation(time, &mVertices, &mVertNormals, vstart, mVertices.size(), false );
809
810                 if(debugTriCollect) debMsgStd("ntlScene::buildScene",DM_MSG,"Done with "<<(*iter)->getName()<<" totTris:"<<mTriangles.size()<<" totVerts:"<<mVertices.size()<<" totNorms:"<<mVertNormals.size(), 4 );
811                 idCnt ++;
812         }
813         if(debugTriCollect) debMsgStd("ntlScene::buildScene",DM_MSG,"End",5);
814
815
816         /* calculate triangle normals, and initialize flags */
817   for (vector<ntlTriangle>::iterator iter = mTriangles.begin();
818        iter != mTriangles.end();
819        iter++) {
820
821                 // calculate normal from triangle points
822                 ntlVec3Gfx normal = 
823                         cross( (ntlVec3Gfx)( (mVertices[(*iter).getPoints()[2]] - mVertices[(*iter).getPoints()[0]]) *-1.0),  // BLITZ minus sign right??
824                         (ntlVec3Gfx)(mVertices[(*iter).getPoints()[1]] - mVertices[(*iter).getPoints()[0]]) );
825                 normalize(normal);
826                 (*iter).setNormal( normal );
827         }
828
829
830
831         // scene geometry built 
832         mSceneBuilt = true;
833
834         // init shaders that require complete geometry
835         if(!mFirstInitDone) {
836                 // only on first init
837                 for (vector<ntlGeometryClass*>::iterator iter = mGeos.begin();
838                                 iter != mGeos.end(); iter++) {
839                         if( (*iter)->getTypeId() & GEOCLASSTID_SHADER ) {
840                                 ntlGeometryShader *geoshad = (ntlGeometryShader*)(*iter);
841                                 if(geoshad->postGeoConstrInit( mpGlob )) {
842                                         errFatal("ntlScene::buildScene","Init failed for object '"<< (*iter)->getName() <<"' !", SIMWORLD_INITERROR );
843                                         return;
844                                 }
845                         }
846                 }
847                 mFirstInitDone = true;
848         }
849
850         // check unused attributes (for classes and objects!)
851   for (vector<ntlGeometryObject*>::iterator iter = mObjects.begin(); iter != mObjects.end(); iter++) {
852                 if((*iter)->getAttributeList()->checkUnusedParams()) {
853                         (*iter)->getAttributeList()->print(); // DEBUG
854                         errFatal("ntlScene::buildScene","Unused params for object '"<< (*iter)->getName() <<"' !", SIMWORLD_INITERROR );
855                         return;
856                 }
857         }
858         for (vector<ntlGeometryClass*>::iterator iter = mGeos.begin(); iter != mGeos.end(); iter++) { 
859                 if((*iter)->getAttributeList()->checkUnusedParams()) {
860                         (*iter)->getAttributeList()->print(); // DEBUG
861                         errFatal("ntlScene::buildScene","Unused params for object '"<< (*iter)->getName() <<"' !", SIMWORLD_INITERROR );
862                         return;
863                 }
864         }
865
866 }
867
868 /******************************************************************************
869  * Prepare the scene triangles and maps for raytracing
870  *****************************************************************************/
871 void ntlScene::prepareScene(double time)
872 {
873         /* init triangles... */
874         buildScene(time, false);
875         // what for currently not used ???
876         if(mpTree != NULL) delete mpTree;
877         mpTree = new ntlTree( 
878 #                       if FSGR_STRICT_DEBUG!=1
879                         mpGlob->getTreeMaxDepth(), mpGlob->getTreeMaxTriangles(), 
880 #                       else
881                         mpGlob->getTreeMaxDepth()/3*2, mpGlob->getTreeMaxTriangles()*2, 
882 #                       endif
883                         this, TRI_GEOMETRY );
884
885         //debMsgStd("ntlScene::prepareScene",DM_MSG,"Stats - tris:"<< (int)mTriangles.size()<<" verts:"<<mVertices.size()<<" vnorms:"<<mVertNormals.size(), 5 );
886 }
887 /******************************************************************************
888  * Do some memory cleaning, when frame is finished
889  *****************************************************************************/
890 void ntlScene::cleanupScene( void )
891 {
892         mTriangles.clear();
893         mVertices.clear();
894         mVertNormals.clear();
895
896         if(mpTree != NULL) delete mpTree;
897         mpTree = NULL;
898 }
899
900
901 /******************************************************************************
902  * Intersect a ray with the scene triangles
903  *****************************************************************************/
904 void ntlScene::intersectScene(const ntlRay &r, gfxReal &distance, ntlVec3Gfx &normal, ntlTriangle *&tri,int flags) const
905 {
906         distance = -1.0;
907   mpGlob->setCounterSceneInter( mpGlob->getCounterSceneInter()+1 );
908         mpTree->intersect(r, distance, normal, tri, flags, false);
909 }
910
911
912
913
914