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