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