- New options for mesh voxelization: shell only (also
[blender.git] / intern / elbeem / intern / ntl_world.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 <sys/stat.h>
12 #include <sstream>
13 #include "utilities.h"
14 #include "ntl_world.h"
15 #include "parametrizer.h"
16
17 // for non-threaded renderViz
18 #ifndef NOGUI
19 #include "../gui/ntl_openglrenderer.h"
20 #include "../gui/guifuncs.h"
21 #include "../gui/frame.h"
22 #endif
23
24
25 /* external parser functions from cfgparser.cxx */
26 #ifndef ELBEEM_PLUGIN
27 /* parse given file as config file */
28 void parseFile(string filename);
29 /* set pointers for parsing */
30 void setPointers( ntlRenderGlobals *setglob);
31 #endif // ELBEEM_PLUGIN
32
33
34 /******************************************************************************
35  * Constructor
36  *****************************************************************************/
37
38 ntlWorld::ntlWorld() {
39         initDefaults();
40 }
41
42 ntlWorld::ntlWorld(string filename, bool commandlineMode) 
43 {
44 #ifndef ELBEEM_PLUGIN
45
46                 initDefaults();
47 #       ifdef NOGUI
48                 commandlineMode = true; // remove warning...
49 #       endif // NOGUI
50
51                 // load config
52                 setPointers( getRenderGlobals() );
53                 parseFile( filename.c_str() );
54 #       ifndef NOGUI
55                 // setup opengl display, save first animation step for start time 
56                 // init after parsing file...
57                 if(!commandlineMode) {
58                         mpOpenGLRenderer = new ntlOpenGLRenderer( mpGlob );
59                 }
60 #       endif // NOGUI
61                 finishWorldInit();
62
63 #else // ELBEEM_PLUGIN
64         errFatal("ntlWorld::init","Cfg file parsing not supported for API version!", SIMWORLD_INITERROR);
65 #endif // ELBEEM_PLUGIN
66 }
67
68
69 int globalDomainCounter = 1;
70 int ntlWorld::addDomain(elbeemSimulationSettings *settings)
71 {
72         // create domain obj
73         SimulationObject *sim = new SimulationObject();
74         char simname[100];
75         snprintf(simname,100,"domain%04d",globalDomainCounter);
76         globalDomainCounter++;
77         sim->setName(std::string(simname));
78         mpGlob->getSims()->push_back( sim );
79
80         // important - add to both, only render scene objects are free'd 
81         mpGlob->getRenderScene()->addGeoClass( sim );
82         mpGlob->getSimScene()->addGeoClass( sim );
83         sim->setGeoStart(ntlVec3Gfx(settings->geoStart[0],settings->geoStart[1],settings->geoStart[2]));
84         sim->setGeoEnd(ntlVec3Gfx(
85                         settings->geoStart[0]+settings->geoSize[0],
86                         settings->geoStart[1]+settings->geoSize[1],
87                         settings->geoStart[2]+settings->geoSize[2] ));
88         // further init in postGeoConstrInit/initializeLbmSimulation of SimulationObject
89         sim->copyElbeemSettings(settings);
90
91         Parametrizer *param = sim->getParametrizer();
92         param->setSize( settings->resolutionxyz );
93         param->setDomainSize( settings->realsize );
94         param->setAniStart( settings->animStart );
95         param->setNormalizedGStar( settings->gstar );
96
97         // init domain channels
98         vector<ParamFloat> valf; 
99         vector<ParamVec> valv; 
100         vector<double> time;
101
102 #define INIT_CHANNEL_FLOAT(channel,size) \
103         valf.clear(); time.clear(); elbeemSimplifyChannelFloat(channel,&size); \
104         for(int i=0; i<size; i++) { valf.push_back( channel[2*i+0] ); time.push_back( channel[2*i+1] ); } 
105 #define INIT_CHANNEL_VEC(channel,size) \
106         valv.clear(); time.clear(); elbeemSimplifyChannelVec3(channel,&size); \
107         for(int i=0; i<size; i++) { valv.push_back( ParamVec(channel[4*i+0],channel[4*i+1],channel[4*i+2]) ); time.push_back( channel[4*i+3] ); } 
108
109         param->setViscosity( settings->viscosity );
110         if((settings->channelViscosity)&&(settings->channelSizeViscosity>0)) {
111                 INIT_CHANNEL_FLOAT(settings->channelViscosity, settings->channelSizeViscosity);
112                 param->initViscosityChannel(valf,time); }
113
114         param->setGravity( ParamVec(settings->gravity[0], settings->gravity[1], settings->gravity[2]) );
115         if((settings->channelGravity)&&(settings->channelSizeGravity>0)) {
116                 INIT_CHANNEL_VEC(settings->channelGravity, settings->channelSizeGravity);
117                 param->initGravityChannel(valv,time); }
118
119         param->setAniFrameTimeChannel( settings->aniFrameTime );
120         if((settings->channelFrameTime)&&(settings->channelSizeFrameTime>0)) {
121                 INIT_CHANNEL_FLOAT(settings->channelFrameTime, settings->channelSizeFrameTime);
122                 param->initAniFrameTimeChannel(valf,time); }
123
124 #undef INIT_CHANNEL_FLOAT
125 #undef INIT_CHANNEL_VEC
126         
127         // might be set by previous domain
128         if(mpGlob->getAniFrames() < settings->noOfFrames)       mpGlob->setAniFrames( settings->noOfFrames );
129         // set additionally to SimulationObject->mOutFilename
130         mpGlob->setOutFilename( settings->outputPath );
131
132         return 0;
133 }
134
135 void ntlWorld::initDefaults()
136 {
137         mStopRenderVisualization = false;
138         mThreadRunning =  false;
139         mSimulationTime = 0.0; 
140         mFirstSim = 1;
141         mSingleStepDebug =  false;
142         mFrameCnt = 0;
143         mpOpenGLRenderer = NULL;
144
145   /* create scene storage */
146   mpGlob = new ntlRenderGlobals();
147   mpLightList = new vector<ntlLightObject*>;
148   mpPropList = new vector<ntlMaterial*>;
149   mpSims = new vector<SimulationObject*>;
150
151   mpGlob->setLightList(mpLightList);
152   mpGlob->setMaterials(mpPropList);
153   mpGlob->setSims(mpSims);
154
155         /* init default material */
156   ntlMaterial *def = GET_GLOBAL_DEFAULT_MATERIAL;
157         mpPropList->push_back( def );
158
159         /* init the scene object */
160         ntlScene *renderscene = new ntlScene( mpGlob, true );
161         mpGlob->setRenderScene( renderscene );
162         // sim scene shouldnt delete objs, may only contain subset
163         ntlScene *simscene = new ntlScene( mpGlob, false );
164         mpGlob->setSimScene( simscene );
165 }
166
167 void ntlWorld::finishWorldInit()
168 {
169         if(! isSimworldOk() ) return;
170
171         // init the scene for the first time
172   long sstartTime = getTime();
173
174         // first init sim scene for geo setup
175         mpGlob->getSimScene()->buildScene(0.0, true);
176         if(! isSimworldOk() ) return;
177         mpGlob->getRenderScene()->buildScene(0.0, true);
178         if(! isSimworldOk() ) return;
179         long sstopTime = getTime();
180         debMsgStd("ntlWorld::ntlWorld",DM_MSG,"Scene build time: "<< getTimeString(sstopTime-sstartTime) <<" ", 10);
181
182         // TODO check simulations, run first steps
183         mFirstSim = -1;
184         if(mpSims->size() > 0) {
185
186                 // use values from first simulation as master time scale
187                 long startTime = getTime();
188                 
189                 // remember first active sim
190                 for(size_t i=0;i<mpSims->size();i++) {
191                         if(!(*mpSims)[i]->getVisible()) continue;
192                         if((*mpSims)[i]->getPanic())    continue;
193
194                         // check largest timestep
195                         if(mFirstSim>=0) {
196                                 if( (*mpSims)[i]->getTimestep() > (*mpSims)[mFirstSim]->getTimestep() ) {
197                                         mFirstSim = i;
198                                         debMsgStd("ntlWorld::ntlWorld",DM_MSG,"First Sim changed: "<<i ,10);
199                                 }
200                         }
201                         // check any valid sim
202                         if(mFirstSim<0) {
203                                 mFirstSim = i;
204                                 debMsgStd("ntlWorld::ntlWorld",DM_MSG,"First Sim: "<<i ,10);
205                         }
206                 }
207
208                 if(mFirstSim>=0) {
209                         debMsgStd("ntlWorld::ntlWorld",DM_MSG,"Anistart Time: "<<(*mpSims)[mFirstSim]->getStartTime() ,10);
210                         while(mSimulationTime < (*mpSims)[mFirstSim]->getStartTime() ) {
211                         debMsgStd("ntlWorld::ntlWorld",DM_MSG,"Anistart Time: "<<(*mpSims)[mFirstSim]->getStartTime()<<" simtime:"<<mSimulationTime ,10);
212                                 advanceSims(-1);
213                         }
214                         long stopTime = getTime();
215
216                         mSimulationTime += (*mpSims)[mFirstSim]->getStartTime();
217                         debMsgStd("ntlWorld::ntlWorld",DM_MSG,"Time for start-sims:"<< getTimeString(stopTime-startTime) , 1);
218 #ifndef NOGUI
219                         guiResetSimulationTimeRange( mSimulationTime );
220 #endif
221                 } else {
222                         if(!mpGlob->getSingleFrameMode()) debMsgStd("ntlWorld::ntlWorld",DM_WARNING,"No active simulations!", 1);
223                 }
224         }
225
226         if(! isSimworldOk() ) return;
227         setElbeemState( SIMWORLD_INITED );
228 }
229
230
231
232 /******************************************************************************
233  * Destructor
234  *****************************************************************************/
235 ntlWorld::~ntlWorld()
236 {
237         delete mpGlob->getRenderScene();
238         delete mpGlob->getSimScene();
239   delete mpGlob;
240   delete mpLightList;
241   delete mpPropList;
242   delete mpSims;
243 #ifndef NOGUI
244         if(mpOpenGLRenderer) delete mpOpenGLRenderer;
245 #endif // NOGUI
246         debMsgStd("ntlWorld",DM_NOTIFY, "ntlWorld done", 10);
247 }
248
249 /******************************************************************************/
250 /*! set single frame rendering to filename */
251 void ntlWorld::setSingleFrameOut(string singleframeFilename) {
252         mpGlob->setSingleFrameMode(true);
253         mpGlob->setSingleFrameFilename(singleframeFilename);
254 }
255
256 /******************************************************************************
257  * render a whole animation (command line mode) 
258  *****************************************************************************/
259
260 // blender interface
261 //#if ELBEEM_BLENDER==1
262 //extern "C" { void simulateThreadIncreaseFrame(void); }
263 //#endif // ELBEEM_BLENDER==1
264
265 int ntlWorld::renderAnimation( void )
266 {
267         // only single pic currently
268         //debMsgStd("ntlWorld::renderAnimation : Warning only simulating...",1);
269         if(mpGlob->getAniFrames() < 0) {
270                 debMsgStd("ntlWorld::renderAnimation",DM_NOTIFY,"No frames to render... ",1);
271                 return 1;
272         }
273
274         if(mFirstSim<0) {
275                 debMsgStd("ntlWorld::renderAnimation",DM_NOTIFY,"No reference animation found...",1);
276                 return 1;
277         } 
278
279         mThreadRunning = true; // not threaded, but still use the same flags
280         if(getElbeemState() == SIMWORLD_INITED) {
281                 renderScene();
282         } else if(getElbeemState() == SIMWORLD_STOP) {
283                 // dont render now, just continue
284                 setElbeemState( SIMWORLD_INITED );
285                 mFrameCnt--; // counted one too many from last abort...
286         } else {
287                 debMsgStd("ntlWorld::renderAnimation",DM_NOTIFY,"Not properly inited, stopping...",1);
288                 return 1;
289         }
290         
291         if(mpSims->size() <= 0) {
292                 debMsgStd("ntlWorld::renderAnimation",DM_NOTIFY,"No simulations found, stopping...",1);
293                 return 1;
294         }
295
296         bool simok = true;
297         for( ; ((mFrameCnt<mpGlob->getAniFrames()) && (!getStopRenderVisualization() ) && (simok)); mFrameCnt++) {
298                 if(!advanceSims(mFrameCnt)) {
299                         renderScene();
300 //#if ELBEEM_BLENDER==1
301                         // update Blender gui display after each frame
302                         //simulateThreadIncreaseFrame();
303 //#endif // ELBEEM_BLENDER==1
304                 } // else means sim panicked, so dont render...
305                 else { simok=false; }
306         }
307         mThreadRunning = false;
308         return 0;
309 }
310
311 /******************************************************************************
312  * render a whole animation (visualization mode) 
313  * this function is run in another thread, and communicates 
314  * with the parent thread via a mutex 
315  *****************************************************************************/
316 int ntlWorld::renderVisualization( bool multiThreaded ) 
317 {
318 #ifndef NOGUI
319         if(getElbeemState() != SIMWORLD_INITED) { return 0; }
320
321         if(multiThreaded) mThreadRunning = true;
322         // TODO, check global state?
323         while(!getStopRenderVisualization()) {
324
325                 if(mpSims->size() <= 0) {
326                         debMsgStd("ntlWorld::renderVisualization",DM_NOTIFY,"No simulations found, stopping...",1);
327                         stopSimulationThread();
328                         break;
329                 }
330
331                 // determine stepsize
332                 if(!mSingleStepDebug) {
333                         long startTime = getTime();
334                         advanceSims(mFrameCnt);
335                         mFrameCnt++;
336                         long stopTime = getTime();
337                         debMsgStd("ntlWorld::renderVisualization",DM_MSG,"Time for t="<<mSimulationTime<<": "<< getTimeString(stopTime-startTime) <<" ", 10);
338                 } else {
339                         double targetTime = mSimulationTime + (*mpSims)[mFirstSim]->getTimestep();
340                         singleStepSims(targetTime);
341
342                         // check paniced sims (normally done by advanceSims
343                         bool allPanic = true;
344                         for(size_t i=0;i<mpSims->size();i++) {
345                                 if(!(*mpSims)[i]->getPanic()) allPanic = false;
346                         }
347                         if(allPanic) {
348                                 warnMsg("ntlWorld::advanceSims","All sims panicked... stopping thread" );
349                                 setStopRenderVisualization( true );
350                         }
351                         if(! isSimworldOk() ) {
352                                 warnMsg("ntlWorld::advanceSims","World state error... stopping" );
353                                 setStopRenderVisualization( true );
354                         }
355                 }
356
357                 // save frame
358                 if(mpOpenGLRenderer) mpOpenGLRenderer->saveAnimationFrame( mSimulationTime );
359                 
360                 // for non-threaded check events
361                 if(!multiThreaded) {
362                         Fl::check();
363       gpElbeemFrame->SceneDisplay->doOnlyForcedRedraw();
364                 }
365
366         }
367         mThreadRunning = false;
368         stopSimulationRestoreGui();
369 #else 
370         multiThreaded = false; // remove warning
371 #endif
372         return 0;
373 }
374 /*! render a single step for viz mode */
375 int ntlWorld::singleStepVisualization( void ) 
376 {
377         mThreadRunning = true;
378         double targetTime = mSimulationTime + (*mpSims)[mFirstSim]->getTimestep();
379         singleStepSims(targetTime);
380         mSimulationTime = (*mpSims)[0]->getCurrentTime();
381
382 #ifndef NOGUI
383         if(mpOpenGLRenderer) mpOpenGLRenderer->saveAnimationFrame( mSimulationTime );
384         Fl::check();
385   gpElbeemFrame->SceneDisplay->doOnlyForcedRedraw();
386         mThreadRunning = false;
387         stopSimulationRestoreGui();
388 #else
389         mThreadRunning = false;
390 #endif // NOGUI
391         return 0;
392 }
393
394 // dont use LBM_EPSILON here, time is always double-precision!
395 #define LBM_TIME_EPSILON 1e-10
396
397 /******************************************************************************
398  * advance simulations by time t 
399  *****************************************************************************/
400 int ntlWorld::advanceSims(int framenum)
401 {
402         bool done = false;
403         bool allPanic = true;
404
405         // stop/quit, dont display/render
406         if(getElbeemState()==SIMWORLD_STOP) { 
407                 return 1;
408         }
409
410         for(size_t i=0;i<mpSims->size();i++) { (*mpSims)[i]->setFrameNum(framenum); }
411         double targetTime = mSimulationTime + (*mpSims)[mFirstSim]->getFrameTime(framenum);
412
413         // time stopped? nothing else to do...
414         if( (*mpSims)[mFirstSim]->getFrameTime(framenum) <= 0.0 ){ 
415                 done=true; allPanic=false; 
416         }
417
418         int gstate = 0;
419 //#if ELBEEM_BLENDER==1
420         // same as solver_main check, but no mutex check here
421         //gstate = getGlobalBakeState();
422         //if(gstate<0) { allPanic = true; done = true; } // this means abort... cause panic
423 //#endif // ELBEEM_BLENDER==1
424
425         // step all the sims, and check for panic
426         debMsgStd("ntlWorld::advanceSims",DM_MSG, " sims "<<mpSims->size()<<" t"<<targetTime<<" done:"<<done<<" panic:"<<allPanic<<" gstate:"<<gstate, 10); // debug // timedebug
427         while(!done) {
428                 double nextTargetTime = (*mpSims)[mFirstSim]->getCurrentTime() + (*mpSims)[mFirstSim]->getTimestep();
429                 singleStepSims(nextTargetTime);
430
431                 // check target times
432                 done = true;
433                 allPanic = false;
434                 
435                 if((*mpSims)[mFirstSim]->getTimestep() <1e-9 ) { 
436                         // safety check, avoid timesteps that are too small
437                         errMsg("ntlWorld::advanceSims","Invalid time step, causing panic! curr:"<<(*mpSims)[mFirstSim]->getCurrentTime()<<" next:"<<nextTargetTime<<", stept:"<< (*mpSims)[mFirstSim]->getTimestep() );
438                         allPanic = true; 
439                 } else {
440                         for(size_t i=0;i<mpSims->size();i++) {
441                                 if(!(*mpSims)[i]->getVisible()) continue;
442                                 if((*mpSims)[i]->getPanic()) allPanic = true; // do any panic now!?
443                                 debMsgStd("ntlWorld::advanceSims",DM_MSG, "Sim "<<i<<", currt:"<<(*mpSims)[i]->getCurrentTime()<<", nt:"<<nextTargetTime<<", panic:"<<(*mpSims)[i]->getPanic()<<", targett:"<<targetTime, 10); // debug // timedebug
444                         } 
445                 }
446                 if( (targetTime - (*mpSims)[mFirstSim]->getCurrentTime()) > LBM_TIME_EPSILON) done=false;
447                 if(allPanic) done = true;
448         }
449
450         if(allPanic) {
451                 warnMsg("ntlWorld::advanceSims","All sims panicked... stopping thread" );
452                 setStopRenderVisualization( true );
453                 return 1;
454         }
455
456         // finish step
457         for(size_t i=0;i<mpSims->size();i++) {
458                 SimulationObject *sim = (*mpSims)[i];
459                 if(!sim->getVisible()) continue;
460                 if(sim->getPanic()) continue;
461                 sim->prepareVisualization();
462         }
463
464         return 0;
465 }
466
467 /* advance simulations by a single step */
468 /* dont check target time, if *targetTime==NULL */
469 void ntlWorld::singleStepSims(double targetTime) {
470         const bool debugTime = false;
471         //double targetTime = mSimulationTime + (*mpSims)[mFirstSim]->getTimestep();
472         if(debugTime) errMsg("ntlWorld::singleStepSims","Target time: "<<targetTime);
473
474         for(size_t i=0;i<mpSims->size();i++) {
475                 SimulationObject *sim = (*mpSims)[i];
476                 if(!sim->getVisible()) continue;
477                 if(sim->getPanic()) continue;
478                 bool done = false;
479                 while(!done) {
480                         // try to prevent round off errs
481                         if(debugTime) errMsg("ntlWorld::singleStepSims","Test sim "<<i<<" curt:"<< sim->getCurrentTime()<<" target:"<<targetTime<<" delta:"<<(targetTime - sim->getCurrentTime())<<" stept:"<<sim->getTimestep()<<" leps:"<<LBM_TIME_EPSILON ); // timedebug
482                         if( (targetTime - sim->getCurrentTime()) > LBM_TIME_EPSILON) {
483                                 if(debugTime) errMsg("ntlWorld::singleStepSims","Stepping sim "<<i<<" t:"<< sim->getCurrentTime()); // timedebug
484                                 sim->step();
485                         } else {
486                                 done = true;
487                         }
488                 }
489         }
490
491         mSimulationTime = (*mpSims)[mFirstSim]->getCurrentTime();
492 #ifndef NOGUI
493         if(mpOpenGLRenderer) mpOpenGLRenderer->notifyOfNextStep(mSimulationTime);
494 #endif // NOGUI
495 }
496
497
498
499
500 /******************************************************************************
501  * Render the current scene
502  * uses the global variables from the parser
503  *****************************************************************************/
504 int ntlWorld::renderScene( void )
505 {
506 #ifndef ELBEEM_PLUGIN
507         char nrStr[5];                                                                                                          // nr conversion 
508         std::ostringstream outfn_conv("");                      // converted ppm with other suffix 
509   ntlRenderGlobals *glob;                       // storage for global rendering parameters 
510   myTime_t timeStart,totalStart,timeEnd;                // measure user running time 
511   myTime_t rendStart,rendEnd;                           // measure user rendering time 
512   glob = mpGlob;
513
514         /* check if picture already exists... */
515         if(!glob->getSingleFrameMode() ) {
516                 snprintf(nrStr, 5, "%04d", glob->getAniCount() );
517                 //outfilename << glob->getOutFilename() <<"_" << nrStr << ".ppm";
518                 outfn_conv  << glob->getOutFilename() <<"_" << nrStr << ".png";
519                 
520                 //if((mpGlob->getDisplayMode() == DM_RAY)&&(mpGlob->getFrameSkip())) {
521                 if(mpGlob->getFrameSkip()) {
522                         struct stat statBuf;
523                         if(stat(outfn_conv.str().c_str(),&statBuf) == 0) {
524                                 errorOut("ntlWorld::renderscene Warning: file "<<outfn_conv.str()<<" already exists - skipping frame..."); 
525                                 glob->setAniCount( glob->getAniCount() +1 );
526                                 return(2);
527                         }
528                 } // RAY mode
529         } else {
530                 // single frame rendering, overwrite if necessary...
531                 outfn_conv << glob->getSingleFrameFilename();
532         }
533
534   /* start program */
535         timeStart = getTime();
536
537         /* build scene geometry, calls buildScene(t,false) */
538         glob->getRenderScene()->prepareScene(mSimulationTime);
539
540   /* start program */
541         totalStart = getTime();
542
543
544         /* view parameters are currently not animated */
545         /* calculate rays through projection plane */
546         ntlVec3Gfx direction = glob->getLookat() - glob->getEye();
547         /* calculate width of screen using perpendicular triangle diven by
548          * viewing direction and screen plane */
549         gfxReal screenWidth = norm(direction)*tan( (glob->getFovy()*0.5/180.0)*M_PI );
550
551         /* calculate vector orthogonal to up and viewing direction */
552         ntlVec3Gfx upVec = glob->getUpVec();
553         ntlVec3Gfx rightVec( cross(upVec,direction) );
554         normalize(rightVec);
555
556         /* calculate screen plane up vector, perpendicular to viewdir and right vec */
557         upVec = ntlVec3Gfx( cross(rightVec,direction) );
558         normalize(upVec);
559
560         /* check if vectors are valid */
561         if( (equal(upVec,ntlVec3Gfx(0.0))) || (equal(rightVec,ntlVec3Gfx(0.0))) ) {
562                 errMsg("ntlWorld::renderScene","Invalid viewpoint vectors! up="<<upVec<<" right="<<rightVec);
563                 return(1);
564         }
565
566         /* length from center to border of screen plane */
567         rightVec *= (screenWidth*glob->getAspect() * -1.0);
568         upVec *= (screenWidth * -1.0);
569
570         /* screen traversal variables */
571         ntlVec3Gfx screenPos;                          /* current position on virtual screen */
572         int Xres = glob->getResX();                  /* X resolution */
573         int Yres = glob->getResY();                  /* Y resolution */
574         ntlVec3Gfx rightStep = (rightVec/(Xres/2.0));  /* one step right for a pixel */
575         ntlVec3Gfx upStep    = (upVec/(Yres/2.0));     /* one step up for a pixel */
576     
577
578         /* anti alias init */
579         char  showAAPic = 0;
580         int   aaDepth = glob->getAADepth();
581         int   aaLength;
582         if(aaDepth>=0) aaLength = (2<<aaDepth);
583         else           aaLength = 0;
584         float aaSensRed   = 0.1;
585         float aaSensGreen = 0.1;
586         float aaSensBlue  = 0.1;
587         int   aaArrayX = aaLength*Xres+1;
588         int   aaArrayY = ( aaLength+1 );
589         ntlColor *aaCol = new ntlColor[ aaArrayX*aaArrayY ];
590         char  *aaUse = new char[ aaArrayX*aaArrayY ];
591
592         /* picture storage */
593         int picX = Xres;
594         int picY = Yres;
595         if(showAAPic) {
596                 picX = Xres *aaLength+1;
597                 picY = Yres *aaLength+1;
598         }
599         ntlColor *finalPic = new ntlColor[picX * picY];
600
601
602         /* reset picture vars */
603         for(int j=0;j<aaArrayY;j++) {
604                 for(int i=0;i<aaArrayX;i++) {
605                         aaCol[j*aaArrayX+i] = ntlColor(0.0, 0.0, 0.0);
606                         aaUse[j*aaArrayX+i] = 0;
607                 }
608         }
609         for(int j=0;j<picY;j++) {
610                 for(int i=0;i<picX;i++) {
611                         finalPic[j*picX+i] = ntlColor(0.0, 0.0, 0.0);
612                 }
613         }
614
615         /* loop over all y lines in screen, from bottom to top because
616          * ppm format wants 0,0 top left */
617         rendStart = getTime();
618         glob->setCounterShades(0);
619         glob->setCounterSceneInter(0);
620         for (int scanline=Yres ; scanline > 0 ; --scanline) {
621     
622                 debugOutInter( "ntlWorld::renderScene: Line "<<scanline<<
623                                                                  " ("<< ((Yres-scanline)*100/Yres) <<"%) ", 2, 2000 );
624                 screenPos = glob->getLookat() + upVec*((2.0*scanline-Yres)/Yres)
625                         - rightVec;
626
627                 /* loop over all pixels in line */
628                 for (int sx=0 ; sx < Xres ; ++sx) {
629
630                         if((sx==glob->getDebugPixelX())&&(scanline==(Yres-glob->getDebugPixelY()) )) {
631                                 // DEBUG!!!
632                                 glob->setDebugOut(10);
633                         } else glob->setDebugOut(0);
634                         
635                         /* compute ray from eye through current pixel into scene... */
636                         ntlColor col;
637                         if(aaDepth<0) {
638                                 ntlVec3Gfx dir(screenPos - glob->getEye());
639                                 ntlRay the_ray(glob->getEye(), getNormalized(dir), 0, 1.0, glob );
640
641                                 /* ...and trace it */
642                                 col = the_ray.shade();
643                         } else {
644                                 /* anti alias */
645                                 int ai,aj;                   /* position in grid */
646                                 int aOrg = sx*aaLength;      /* grid offset x */
647                                 int currStep = aaLength;     /* step size */
648                                 char colDiff = 1;            /* do colors still differ too much? */
649                                 ntlColor minCol,maxCol;         /* minimum and maximum Color Values */
650                                 minCol = ntlColor(1.0,1.0,1.0);
651                                 maxCol = ntlColor(0.0,0.0,0.0);
652
653                                 while((colDiff) && (currStep>0)) {
654                                         colDiff = 0;
655             
656                                         for(aj = 0;aj<=aaLength;aj+= currStep) {
657                                                 for(ai = 0;ai<=aaLength;ai+= currStep) {
658
659                                                         /* shade pixel if not done */
660                                                         if(aaUse[aj*aaArrayX +ai +aOrg] == 0) {
661                                                                 aaUse[aj*aaArrayX +ai +aOrg] = 1;
662                                                                 ntlVec3Gfx aaPos( screenPos +
663                                                                                                                                 (rightStep * (ai- aaLength/2)/(gfxReal)aaLength ) +
664                                                                                                                                 (upStep    * (aj- aaLength/2)/(gfxReal)aaLength ) );
665
666                                                                 ntlVec3Gfx dir(aaPos - glob->getEye());
667                                                                 ntlRay the_ray(glob->getEye(), getNormalized(dir), 0, 1.0, glob );
668
669                                                                 /* ...and trace it */
670                                                                 ntlColor newCol= the_ray.shade();
671                                                                 aaCol[aj*aaArrayX +ai +aOrg]= newCol;
672                                                         } /* not used? */
673
674                                                 }
675                                         }
676
677                                         /* check color differences */
678                                         for(aj = 0;aj<aaLength;aj+= currStep) {
679                                                 for(ai = 0;ai<aaLength;ai+= currStep) {
680
681                                                         char thisColDiff = 0;
682                                                         if( 
683                                                                  (fabs(aaCol[aj*aaArrayX +ai +aOrg][0] - 
684                                                                                          aaCol[(aj+0)*aaArrayX +(ai+currStep) +aOrg][0])> aaSensRed ) ||
685                                                                  (fabs(aaCol[aj*aaArrayX +ai +aOrg][1] - 
686                                                                                          aaCol[(aj+0)*aaArrayX +(ai+currStep) +aOrg][1])> aaSensGreen ) ||
687                                                                  (fabs(aaCol[aj*aaArrayX +ai +aOrg][2] - 
688                                                                                          aaCol[(aj+0)*aaArrayX +(ai+currStep) +aOrg][2])> aaSensBlue ) ) {
689                                                                 thisColDiff = 1;
690                                                         } else
691                                                                 if( 
692                                                                          (fabs(aaCol[aj*aaArrayX +ai +aOrg][0] - 
693                                                                                                  aaCol[(aj+currStep)*aaArrayX +(ai+0) +aOrg][0])> aaSensRed ) ||
694                                                                          (fabs(aaCol[aj*aaArrayX +ai +aOrg][1] - 
695                                                                                                  aaCol[(aj+currStep)*aaArrayX +(ai+0) +aOrg][1])> aaSensGreen ) ||
696                                                                          (fabs(aaCol[aj*aaArrayX +ai +aOrg][2] - 
697                                                                                                  aaCol[(aj+currStep)*aaArrayX +(ai+0) +aOrg][2])> aaSensBlue ) ) {
698                                                                         thisColDiff = 1;
699                                                                 } else
700                                                                         if( 
701                                                                                  (fabs(aaCol[aj*aaArrayX +ai +aOrg][0] - 
702                                                                                                          aaCol[(aj+currStep)*aaArrayX +(ai+currStep) +aOrg][0])> aaSensRed ) ||
703                                                                                  (fabs(aaCol[aj*aaArrayX +ai +aOrg][1] - 
704                                                                                                          aaCol[(aj+currStep)*aaArrayX +(ai+currStep) +aOrg][1])> aaSensGreen ) ||
705                                                                                  (fabs(aaCol[aj*aaArrayX +ai +aOrg][2] - 
706                                                                                                          aaCol[(aj+currStep)*aaArrayX +(ai+currStep) +aOrg][2])> aaSensBlue ) ) {
707                                                                                 thisColDiff = 1;
708                                                                         } 
709
710                                                         //colDiff =1;
711                                                         if(thisColDiff) {
712                                                                 /* set diff flag */
713                                                                 colDiff = thisColDiff;
714                                                                 for(int bj=aj;bj<=aj+currStep;bj++) {
715                                                                         for(int bi=ai;bi<=ai+currStep;bi++) {
716                                                                                 if(aaUse[bj*aaArrayX +bi +aOrg]==2) {
717                                                                                         //if(showAAPic) 
718                                                                                         aaUse[bj*aaArrayX +bi +aOrg] = 0;
719                                                                                 }
720                                                                         }
721                                                                 }
722                                                         } else {
723                                                                 /* set all values */
724                                                                 ntlColor avgCol = (
725                                                                                                                                          aaCol[(aj+0       )*aaArrayX +(ai+0       ) +aOrg] +
726                                                                                                                                          aaCol[(aj+0       )*aaArrayX +(ai+currStep) +aOrg] +
727                                                                                                                                          aaCol[(aj+currStep)*aaArrayX +(ai+0       ) +aOrg] +
728                                                                                                                                          aaCol[(aj+currStep)*aaArrayX +(ai+currStep) +aOrg] ) *0.25;
729                                                                 for(int bj=aj;bj<=aj+currStep;bj++) {
730                                                                         for(int bi=ai;bi<=ai+currStep;bi++) {
731                                                                                 if(aaUse[bj*aaArrayX +bi +aOrg]==0) {
732                                                                                         aaCol[bj*aaArrayX +bi +aOrg] = avgCol; 
733                                                                                         aaUse[bj*aaArrayX +bi +aOrg] = 2;
734                                                                                 }
735                                                                         }
736                                                                 }
737                                                         } /* smaller values set */
738
739                                                 }
740                                         }
741
742                                         /* half step size */
743                                         currStep /= 2;
744
745                                 } /* repeat until diff not too big */
746
747                                 /* get average color */
748                                 gfxReal colNum = 0.0;
749                                 col = ntlColor(0.0, 0.0, 0.0);
750                                 for(aj = 0;aj<=aaLength;aj++) {
751                                         for(ai = 0;ai<=aaLength;ai++) {
752                                                 col += aaCol[aj*aaArrayX +ai +aOrg];
753                                                 colNum += 1.0;
754                                         }
755                                 }
756                                 col /= colNum;
757
758                         }
759
760                   /* mark pixels with debugging */
761                         if( glob->getDebugOut() > 0) col = ntlColor(0,1,0);
762
763                         /* store pixel */
764                         if(!showAAPic) {
765                                 finalPic[(scanline-1)*picX+sx] = col; 
766                         }
767                         screenPos +=  rightStep;
768
769                 } /* foreach x */
770
771                 /* init aa array */
772                 if(showAAPic) {
773                         for(int j=0;j<=aaArrayY-1;j++) {
774                                 for(int i=0;i<=aaArrayX-1;i++) {
775                                         if(aaUse[j*aaArrayX +i]==1) finalPic[((scanline-1)*aaLength +j)*picX+i][0] = 1.0;
776                                 }
777                         }
778                 }
779
780                 for(int i=0;i<aaArrayX;i++) {
781                         aaCol[(aaArrayY-1)*aaArrayX+i] = aaCol[0*aaArrayX+i];
782                         aaUse[(aaArrayY-1)*aaArrayX+i] = aaUse[0*aaArrayX+i];
783                 }
784                 for(int j=0;j<aaArrayY-1;j++) {
785                         for(int i=0;i<aaArrayX;i++) {
786                                 aaCol[j*aaArrayX+i] = ntlColor(0.0, 0.0, 0.0);
787                                 aaUse[j*aaArrayX+i] = 0;
788                         }
789                 }
790
791         } /* foreach y */
792         rendEnd = getTime();
793
794
795         /* write png file */
796         {
797                 int w = picX;
798                 int h = picY;
799
800                 unsigned rowbytes = w*4;
801                 unsigned char *screenbuf, **rows;
802                 screenbuf = (unsigned char*)malloc( h*rowbytes );
803                 rows = (unsigned char**)malloc( h*sizeof(unsigned char*) );
804                 unsigned char *filler = screenbuf;
805
806                 // cutoff color values 0..1
807                 for(int j=0;j<h;j++) {
808                         for(int i=0;i<w;i++) {
809                                 ntlColor col = finalPic[j*w+i];
810                                 for (unsigned int cc=0; cc<3; cc++) {
811                                         if(col[cc] <= 0.0) col[cc] = 0.0;
812                                         if(col[cc] >= 1.0) col[cc] = 1.0;
813                                 }
814                                 *filler = (unsigned char)( col[0]*255.0 ); 
815                                 filler++;
816                                 *filler = (unsigned char)( col[1]*255.0 ); 
817                                 filler++;
818                                 *filler = (unsigned char)( col[2]*255.0 ); 
819                                 filler++;
820                                 *filler = (unsigned char)( 255.0 ); 
821                                 filler++; // alpha channel
822                         }
823                 }
824
825                 for(int i = 0; i < h; i++) rows[i] = &screenbuf[ (h - i - 1)*rowbytes ];
826 #ifndef NOPNG
827                 writePng(outfn_conv.str().c_str(), rows, w, h);
828 #else // NOPNG
829                 debMsgStd("ntlWorld::renderScene",DM_NOTIFY, "No PNG linked, no picture...", 1);
830 #endif // NOPNG
831         }
832
833
834         // next frame 
835         glob->setAniCount( glob->getAniCount() +1 );
836
837         // done 
838         timeEnd = getTime();
839
840         char resout[1024];
841         snprintf(resout,1024, "NTL Done %s, frame %d/%d (%s scene, %s raytracing, %s total, %d shades, %d i.s.'s)!\n", 
842                                  outfn_conv.str().c_str(), (glob->getAniCount()), (glob->getAniFrames()+1),
843                                  getTimeString(totalStart-timeStart).c_str(), getTimeString(rendEnd-rendStart).c_str(), getTimeString(timeEnd-timeStart).c_str(),
844                                  glob->getCounterShades(),
845                                  glob->getCounterSceneInter() );
846         debMsgStd("ntlWorld::renderScene",DM_MSG, resout, 1 );
847
848         /* clean stuff up */
849         delete [] aaCol;
850         delete [] aaUse;
851         delete [] finalPic;
852         glob->getRenderScene()->cleanupScene();
853
854         if(mpGlob->getSingleFrameMode() ) {
855                 debMsgStd("ntlWorld::renderScene",DM_NOTIFY, "Single frame mode done...", 1 );
856                 return 1;
857         }
858 #endif // ELBEEM_PLUGIN
859         return 0;
860 }
861
862
863 /******************************************************************************
864  * renderglobals
865  *****************************************************************************/
866
867
868 /*****************************************************************************/
869 /* Constructor with standard value init */
870 ntlRenderGlobals::ntlRenderGlobals() :
871         mpRenderScene(NULL), mpSimScene(NULL),
872   mpLightList( NULL ), mpMaterials( NULL ), mpSims( NULL ),
873   mResX(320), mResY(200), mAADepth(-1), mMaxColVal(255), 
874   mRayMaxDepth( 5 ),
875   mvEye(0.0,0.0,5.0), mvLookat(0.0,0.0,0.0), mvUpvec(0.0,1.0,0.0), 
876   mAspect(320.0/200.0), 
877   mFovy(45), mcBackgr(0.0,0.0,0.0), mcAmbientLight(0.0,0.0,0.0), 
878   mDebugOut( 0 ),
879   mAniStart(0), mAniFrames( -1 ), mAniCount( 0 ),
880         mFrameSkip( 0 ),
881   mCounterRays( 0 ), mCounterShades( 0 ), mCounterSceneInter( 0 ),
882         mOutFilename( "pic" ),
883         mTreeMaxDepth( 30 ), mTreeMaxTriangles( 30 ),
884         mpOpenGlAttr(NULL),
885         mpBlenderAttr(NULL),
886         mTestSphereEnabled( false ),
887         mDebugPixelX( -1 ), mDebugPixelY( -1 ), mTestMode(false),
888         mSingleFrameMode(false), mSingleFrameFilename("")
889         //,mpRndDirections( NULL ), mpRndRoulette( NULL )
890
891         // create internal attribute list for opengl renderer
892         mpOpenGlAttr = new AttributeList("__ntlOpenGLRenderer");
893         mpBlenderAttr = new AttributeList("__ntlBlenderAttr");
894 };
895
896
897 /*****************************************************************************/
898 /* Destructor */
899 ntlRenderGlobals::~ntlRenderGlobals() {
900         if(mpOpenGlAttr) delete mpOpenGlAttr;
901         if(mpBlenderAttr) delete mpBlenderAttr;
902 }
903
904
905 /*****************************************************************************/
906 //! get the next random photon direction
907 //ntlVec3Gfx ntlRenderGlobals::getRandomDirection( void ) { 
908         //return ntlVec3Gfx( 
909                         //(mpRndDirections->getGfxReal()-0.5), 
910                         //(mpRndDirections->getGfxReal()-0.5),  
911                         //(mpRndDirections->getGfxReal()-0.5) ); 
912 //} 
913
914