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