source: GTP/trunk/Lib/Vis/Preprocessing/src/SamplingStrategy.cpp @ 2010

Revision 2010, 19.1 KB checked in by bittner, 17 years ago (diff)
Line 
1#include "SamplingStrategy.h"
2#include "Ray.h"
3#include "Intersectable.h"
4#include "Preprocessor.h"
5#include "ViewCellsManager.h"
6#include "AxisAlignedBox3.h"
7#include "RssTree.h"
8#include "Mutation.h"
9
10namespace GtpVisibilityPreprocessor {
11
12//HaltonSequence SamplingStrategy::sHalton;
13
14HaltonSequence ObjectBasedDistribution::sHalton;
15HaltonSequence MixtureDistribution::sHalton;
16HaltonSequence GlobalLinesDistribution::sHalton;
17HaltonSequence SpatialBoxBasedDistribution::sHalton;
18HaltonSequence ObjectDirectionBasedDistribution::sHalton;
19HaltonSequence DirectionBasedDistribution::sHalton;
20HaltonSequence HwGlobalLinesDistribution::sHalton;
21
22HaltonSequence ViewCellBasedDistribution::sHalton;
23
24SamplingStrategy::SamplingStrategy(Preprocessor &preprocessor):
25mPreprocessor(preprocessor),
26mRatio(1.0f), 
27mTotalRays(0),
28mTotalContribution(0.0f)
29
30}
31
32
33SamplingStrategy::~SamplingStrategy()
34{
35}
36
37int
38SamplingStrategy::GenerateSamples(const int number,
39                                                                  SimpleRayContainer &rays)
40{
41        SimpleRay ray;
42        int samples = 0;
43        int i = 0;
44        const int maxTries = 20;
45        // tmp changed matt. Q: should one rejected sample
46        // terminate the whole method?
47        for (; i < number; i++)
48          {
49                int j = 0;
50                bool sampleGenerated = false;
51               
52                for (j = 0; !sampleGenerated && (j < maxTries); ++ j)
53                  {
54                        sampleGenerated = GenerateSample(ray);
55                       
56                        if (sampleGenerated)
57                          {             
58                                ++ samples;
59                                rays.push_back(ray);
60                          }
61                  }
62          }
63
64
65       
66
67        return samples;
68}
69
70
71/*********************************************************************/
72/*            Individual sampling strategies implementation          */
73/*********************************************************************/
74
75
76bool ObjectBasedDistribution::GenerateSample(SimpleRay &ray)
77{
78  Vector3 origin, direction;
79 
80  float r[5];
81  sHalton.GetNext(5, r);
82 
83  mPreprocessor.mViewCellsManager->GetViewPoint(origin,
84                                                                                                Vector3(r[2],r[3],r[4]));
85 
86
87  Vector3 point, normal;
88 
89  r[0] *= (float)mPreprocessor.mObjects.size() - 1;
90  const int i = (int)r[0];
91 
92  Intersectable *object = mPreprocessor.mObjects[i];
93 
94  // take the remainder as a parameter over objects surface
95  r[0] -= (float)i;
96 
97  object->GetRandomSurfacePoint(r[0], r[1], point, normal);
98 
99  direction = point - origin;
100 
101  const float c = Magnitude(direction);
102 
103  if (c <= Limits::Small)
104        return false;
105 
106  // $$ jb the pdf is yet not correct for all sampling methods!
107  const float pdf = 1.0f;
108 
109  direction *= 1.0f / c;
110  ray = SimpleRay(origin, direction, OBJECT_BASED_DISTRIBUTION, pdf);
111 
112  return true;
113}
114
115
116bool
117ObjectDirectionBasedDistribution::GenerateSample(SimpleRay &ray)
118{       
119  Vector3 origin, direction;
120
121
122  float r[4];
123  sHalton.GetNext(4, r);
124
125  r[0] *= (float)mPreprocessor.mObjects.size() - 1;
126  const int i = (int)r[0];
127 
128  Intersectable *object = mPreprocessor.mObjects[i];
129 
130  // take the remainder as a parameter over objects surface
131  r[0] -= (float)i;
132
133  Vector3 normal;
134
135  object->GetRandomSurfacePoint(r[0], r[1], origin, normal);
136 
137  direction = Normalize(CosineRandomVector(r[2], r[3], normal));
138 
139  origin += 1e-2f*direction;
140 
141  // $$ jb the pdf is yet not correct for all sampling methods!
142  const float pdf = 1.0f;
143 
144  ray = SimpleRay(origin, direction, OBJECT_DIRECTION_BASED_DISTRIBUTION, pdf);
145 
146  return true;
147}
148
149
150bool DirectionBasedDistribution::GenerateSample(SimpleRay &ray)
151{       
152
153  float r[5];
154  sHalton.GetNext(5, r);
155
156  Vector3 origin, direction;
157  mPreprocessor.mViewCellsManager->GetViewPoint(origin,
158                                                                                                Vector3(r[2],r[3],r[4])
159                                                                                                );
160 
161  direction = UniformRandomVector(r[0], r[1]);
162  const float c = Magnitude(direction);
163 
164  if (c <= Limits::Small)
165        return false;
166 
167  const float pdf = 1.0f;
168 
169  direction *= 1.0f / c;
170  ray = SimpleRay(origin, direction, DIRECTION_BASED_DISTRIBUTION, pdf);
171 
172  return true;
173}
174
175
176bool DirectionBoxBasedDistribution::GenerateSample(SimpleRay &ray)
177{
178        Vector3 origin, direction;
179        mPreprocessor.mViewCellsManager->GetViewPoint(origin);
180
181        const float alpha = RandomValue(0.0f, 2.0f * (float)M_PI);
182        const float beta = RandomValue((float)-M_PI * 0.5f, (float)M_PI * 0.5f);
183       
184        direction = VssRay::GetDirection(alpha, beta);
185       
186        const float c = Magnitude(direction);
187
188        if (c <= Limits::Small)
189                return false;
190
191        const float pdf = 1.0f;
192
193        direction *= 1.0f / c;
194        ray = SimpleRay(origin, direction, DIRECTION_BOX_BASED_DISTRIBUTION, pdf);
195
196        return true;
197}
198
199
200bool SpatialBoxBasedDistribution::GenerateSample(SimpleRay &ray)
201{
202  Vector3 origin, direction;
203
204  float r[6];
205  sHalton.GetNext(6, r);
206  mPreprocessor.mViewCellsManager->GetViewPoint(origin, Vector3(r[0],r[1],r[2]));
207 
208  direction = mPreprocessor.mKdTree->GetBox().GetRandomPoint(Vector3(r[3],
209                                                                                                                                         r[4],
210                                                                                                                                         r[5])
211                                                                                                                                         ) - origin;
212  //cout << "z";
213  const float c = Magnitude(direction);
214 
215  if (c <= Limits::Small)
216        return false;
217 
218  const float pdf = 1.0f;
219 
220  direction *= 1.0f / c;
221  ray = SimpleRay(origin, direction, SPATIAL_BOX_BASED_DISTRIBUTION, pdf);
222 
223  return true;
224}
225
226
227bool ReverseObjectBasedDistribution::GenerateSample(SimpleRay &ray)
228{
229        Vector3 origin, direction;
230
231        mPreprocessor.mViewCellsManager->GetViewPoint(origin);
232
233        Vector3 point;
234        Vector3 normal;
235       
236        const int i = (int)RandomValue(0, (float)mPreprocessor.mObjects.size() - 0.5f);
237
238        Intersectable *object = mPreprocessor.mObjects[i];
239
240        object->GetRandomSurfacePoint(point, normal);
241       
242        direction = origin - point;
243       
244        // $$ jb the pdf is yet not correct for all sampling methods!
245        const float c = Magnitude(direction);
246       
247        if ((c <= Limits::Small) || (DotProd(direction, normal) < 0))
248        {
249                return false;
250        }
251
252        // $$ jb the pdf is yet not correct for all sampling methods!
253        const float pdf = 1.0f;
254        //cout << "p: " << point << " ";
255        direction *= 1.0f / c;
256        // a little offset
257        point += direction * 0.001f;
258
259        ray = SimpleRay(point, direction, REVERSE_OBJECT_BASED_DISTRIBUTION, pdf);
260       
261        return true;
262}
263
264
265bool ViewCellBorderBasedDistribution::GenerateSample(SimpleRay &ray)
266{
267        Vector3 origin, direction;
268
269        ViewCellContainer &viewCells = mPreprocessor.mViewCellsManager->GetViewCells();
270
271        Vector3 point;
272        Vector3 normal, normal2;
273       
274        const int vcIdx = (int)RandomValue(0, (float)viewCells.size() - 0.5f);
275        const int objIdx = (int)RandomValue(0, (float)mPreprocessor.mObjects.size() - 0.5f);
276
277        Intersectable *object = mPreprocessor.mObjects[objIdx];
278        ViewCell *viewCell = viewCells[vcIdx];
279
280        //cout << "vc: " << vcIdx << endl;
281        //cout << "obj: " << objIdx << endl;
282
283        object->GetRandomSurfacePoint(point, normal);
284        viewCell->GetRandomEdgePoint(origin, normal2);
285
286        direction = point - origin;
287
288        // $$ jb the pdf is yet not correct for all sampling methods!
289        const float c = Magnitude(direction);
290
291        if ((c <= Limits::Small) /*|| (DotProd(direction, normal) < 0)*/)
292        {
293                return false;
294        }
295
296        // $$ jb the pdf is yet not correct for all sampling methods!
297        const float pdf = 1.0f;
298        //cout << "p: " << point << " ";
299        direction *= 1.0f / c;
300        ray = SimpleRay(origin, direction, VIEWCELL_BORDER_BASED_DISTRIBUTION, pdf);
301
302        //cout << "ray: " << ray.mOrigin << " " << ray.mDirection << endl;
303
304        return true;
305}
306
307
308#if 0
309bool ObjectsInteriorDistribution::GenerateSample(SimpleRay &ray)
310{
311        Vector3 origin, direction;
312
313        // get random object
314        const int i = RandomValue(0, mPreprocessor.mObjects.size() - 1);
315
316        const Intersectable *obj = mPreprocessor.mObjects[i];
317
318        // note: if we load the polygons as meshes,
319        // asymtotically every second sample is lost!
320        origin = obj->GetBox().GetRandomPoint();
321
322        // uniformly distributed direction
323        direction = UniformRandomVector();
324
325        const float c = Magnitude(direction);
326
327        if (c <= Limits::Small)
328                return false;
329
330        const float pdf = 1.0f;
331
332        direction *= 1.0f / c;
333        ray = SimpleRay(origin, direction, pdf);
334
335        return true;
336}
337
338#endif
339
340
341bool ReverseViewSpaceBorderBasedDistribution::GenerateSample(SimpleRay &ray)
342{
343        Vector3 origin, direction;
344
345        origin = mPreprocessor.mViewCellsManager->GetViewSpaceBox().GetRandomSurfacePoint();
346
347        Vector3 point;
348        Vector3 normal;
349       
350        const int i = (int)RandomValue(0, (float)mPreprocessor.mObjects.size() - 0.5f);
351
352        Intersectable *object = mPreprocessor.mObjects[i];
353
354        object->GetRandomSurfacePoint(point, normal);
355       
356        direction = origin - point;
357       
358        // $$ jb the pdf is yet not correct for all sampling methods!
359        const float c = Magnitude(direction);
360       
361        if ((c <= Limits::Small) || (DotProd(direction, normal) < 0))
362        {
363                return false;
364        }
365
366        // $$ jb the pdf is yet not correct for all sampling methods!
367        const float pdf = 1.0f;
368        //cout << "p: " << point << " ";
369        direction *= 1.0f / c;
370        // a little offset
371        point += direction * 0.001f;
372
373        ray = SimpleRay(point, direction, REVERSE_VIEWSPACE_BORDER_BASED_DISTRIBUTION, pdf);
374       
375        return true;
376}
377
378
379bool ViewSpaceBorderBasedDistribution::GenerateSample(SimpleRay &ray)
380{
381        Vector3 origin, direction;
382
383        origin = mPreprocessor.mViewCellsManager->GetViewSpaceBox().GetRandomSurfacePoint();
384
385        Vector3 point;
386        Vector3 normal;
387        //cout << "w";
388        const int i = (int)RandomValue(0, (float)mPreprocessor.mObjects.size() - 0.5f);
389
390        Intersectable *object = mPreprocessor.mObjects[i];
391
392        object->GetRandomSurfacePoint(point, normal);
393        direction = point - origin;
394
395        // $$ jb the pdf is yet not correct for all sampling methods!
396        const float c = Magnitude(direction);
397
398        if (c <= Limits::Small)
399                return false;
400
401        // $$ jb the pdf is yet not correct for all sampling methods!
402        const float pdf = 1.0f;
403       
404        direction *= 1.0f / c;
405
406        // a little offset
407        origin += direction * 0.001f;
408
409        ray = SimpleRay(origin, direction, VIEWSPACE_BORDER_BASED_DISTRIBUTION, pdf);
410
411        return true;
412}
413
414
415bool
416GlobalLinesDistribution::GenerateSample(SimpleRay &ray)
417{
418  Vector3 origin, termination, direction;
419
420  float radius = 0.5f*Magnitude(mPreprocessor.mViewCellsManager->GetViewSpaceBox().Size());
421  Vector3 center = mPreprocessor.mViewCellsManager->GetViewSpaceBox().Center();
422
423  const int tries = 1000;
424  int i;
425  for (i=0; i < tries; i++) {
426        float r[4];
427        sHalton.GetNext(4, r);
428       
429        origin = center + (radius*UniformRandomVector(r[0], r[1]));
430        termination = center + (radius*UniformRandomVector(r[2], r[3]));
431       
432        direction = termination - origin;
433       
434       
435        // $$ jb the pdf is yet not correct for all sampling methods!
436        const float c = Magnitude(direction);
437        if (c <= Limits::Small)
438          return false;
439       
440        direction *= 1.0f / c;
441
442        // check if the ray intersects the view space box
443        static Ray ray;
444        ray.Init(origin, direction, Ray::LOCAL_RAY);   
445       
446        float tmin, tmax;
447        if (mPreprocessor.mViewCellsManager->
448                GetViewSpaceBox().ComputeMinMaxT(ray, &tmin, &tmax) && (tmin < tmax))
449          break;
450  }
451 
452  if (i!=tries) {
453        // $$ jb the pdf is yet not correct for all sampling methods!
454        const float pdf = 1.0f;
455       
456       
457        ray = SimpleRay(origin, direction, GLOBAL_LINES_DISTRIBUTION, pdf);
458        ray.mType = Ray::GLOBAL_RAY;
459        return true;
460  }
461 
462  return false;
463}
464
465
466  // has to called before first usage
467void
468MixtureDistribution::Init()
469{
470  for (int i=0; i < mDistributions.size(); i++) {
471        // small non-zero value
472        mDistributions[i]->mRays = 1;
473        mDistributions[i]->mGeneratedRays = 1;
474        // unit contribution per ray
475        if (1 || mDistributions[i]->mType != RSS_BASED_DISTRIBUTION)
476          mDistributions[i]->mContribution = 1.0f;
477        else
478          mDistributions[i]->mContribution = 0.0f;
479  }
480  UpdateRatios();
481}
482
483void
484MixtureDistribution::Reset()
485{
486  for (int i=0; i < mDistributions.size(); i++) {
487        // small non-zero value
488        mDistributions[i]->mTotalRays = 0;
489        // unit contribution per ray
490        mDistributions[i]->mTotalContribution = 0.0f;
491  }
492  UpdateRatios();
493}
494
495// Generate a new sample according to a mixture distribution
496bool
497MixtureDistribution::GenerateSample(SimpleRay &ray)
498{
499  float r;
500  sHalton.GetNext(1, &r);
501  int i;
502  // pickup a distribution
503  for (i=0; i < mDistributions.size()-1; i++)
504        if (r < mDistributions[i]->mRatio)
505          break;
506
507  bool result = mDistributions[i]->GenerateSample(ray);
508
509  if (result)
510        mDistributions[i]->mGeneratedRays++;
511 
512  return result;
513}
514
515  // add contributions of the sample to the strategies
516void
517MixtureDistribution::ComputeContributions(VssRayContainer &vssRays)
518{
519  int i;
520 
521  VssRayContainer::iterator it = vssRays.begin();
522
523  for (i=0; i < mDistributions.size(); i++) {
524        mDistributions[i]->mContribution = 0;
525        mDistributions[i]->mRays = 0;
526  }
527
528  for(; it != vssRays.end(); ++it) {
529        VssRay *ray = *it;
530        for (i=0; i < mDistributions.size()-1; i++) {
531          if (mDistributions[i]->mType == ray->mDistribution)
532                break;
533        }
534 
535        float contribution =
536          mPreprocessor.mViewCellsManager->ComputeSampleContribution(*ray,
537                                                                                                                                 true,
538                                                                                                                                 false);
539
540        mDistributions[i]->mContribution += contribution;
541        mDistributions[i]->mRays ++;
542       
543        mDistributions[i]->mTotalContribution += contribution;
544        mDistributions[i]->mTotalRays ++;
545  }
546
547 
548  UpdateRatios();
549
550}
551
552void
553MixtureDistribution::UpdateDistributions(VssRayContainer &vssRays)
554{
555  // now update the distributions with all the rays
556  for (int i=0; i < mDistributions.size(); i++) {
557        mDistributions[i]->Update(vssRays);
558  }
559}
560
561#define RAY_CAST_TIME 0.7f
562#define VIEWCELL_CAST_TIME 0.3f
563
564void
565MixtureDistribution::UpdateRatios()
566{
567  // now compute importance (ratio) of all distributions
568  float sum = 0.0f;
569  int i;
570  for (i=0; i < mDistributions.size(); i++) {
571        cout<<i<<": c="<<mDistributions[i]->mContribution<<
572          " rays="<<mDistributions[i]->mRays<<endl;
573        float importance = 0.0f;
574        if (mDistributions[i]->mRays != 0) {
575          //importance = pow(mDistributions[i]->mContribution/mDistributions[i]->mRays, 2);
576          importance = mDistributions[i]->mContribution/
577                (RAY_CAST_TIME*mDistributions[i]->mGeneratedRays +
578                 VIEWCELL_CAST_TIME*mDistributions[i]->mRays);
579        }
580        mDistributions[i]->mRatio = importance;
581        sum += importance;
582  }
583
584  if (sum == 0.0f)
585        sum = Limits::Small;
586 
587  const float minratio = 0.01f;
588 
589  for (i=0; i < mDistributions.size(); i++) {
590        mDistributions[i]->mRatio /= sum;
591        if (mDistributions[i]->mRatio < minratio)
592          mDistributions[i]->mRatio = minratio;
593  }
594
595  // recaluate the sum after clip
596  sum = 0.0f;
597  for (i=0; i < mDistributions.size(); i++)
598        sum += mDistributions[i]->mRatio;
599
600  for (i=0; i < mDistributions.size(); i++)
601        mDistributions[i]->mRatio /= sum;
602 
603  for (i=1; i < mDistributions.size(); i++) {
604        mDistributions[i]->mRatio = mDistributions[i-1]->mRatio + mDistributions[i]->mRatio;
605  }
606 
607  cout<<"ratios: ";
608  float last = 0.0f;
609  for (i=0; i < mDistributions.size(); i++) {
610        cout<<mDistributions[i]->mRatio-last<<" ";
611        last = mDistributions[i]->mRatio;
612  }
613  cout<<endl;
614}
615
616
617
618bool
619MixtureDistribution::Construct(char *str)
620{
621  char *curr = str;
622
623  while (1) {
624        char *e = strchr(curr,'+');
625        if (e!=NULL) {
626          *e=0;
627        }
628       
629        if (strcmp(curr, "rss")==0) {
630          mDistributions.push_back(new RssBasedDistribution(mPreprocessor));
631        } else
632          if (strcmp(curr, "object")==0) {
633                mDistributions.push_back(new ObjectBasedDistribution(mPreprocessor));
634          } else
635                if (strcmp(curr, "spatial")==0) {
636                  mDistributions.push_back(new SpatialBoxBasedDistribution(mPreprocessor));
637                } else
638                  if (strcmp(curr, "global")==0) {
639                        mDistributions.push_back(new GlobalLinesDistribution(mPreprocessor));
640                  } else
641                        if (strcmp(curr, "direction")==0) {
642                          mDistributions.push_back(new DirectionBasedDistribution(mPreprocessor));
643                        } else
644                          if (strcmp(curr, "object_direction")==0) {
645                                mDistributions.push_back(new ObjectDirectionBasedDistribution(mPreprocessor));
646                          } else
647                                if (strcmp(curr, "reverse_object")==0) {
648                                  mDistributions.push_back(new ReverseObjectBasedDistribution(mPreprocessor));
649                                } else
650                                  if (strcmp(curr, "reverse_viewspace_border")==0) {
651                                        mDistributions.push_back(new ReverseViewSpaceBorderBasedDistribution(mPreprocessor));
652                                  } else
653                                        if (strcmp(curr, "mutation")==0) {
654                                          // temp matt: still no mutationstrategy!
655                                          mDistributions.push_back(new MutationBasedDistribution(mPreprocessor));
656                                        }
657       
658       
659        if (e==NULL)
660          break;
661        curr = e+1;
662  }
663
664  Init();
665  return true;
666}
667
668int
669MixtureDistribution::GenerateSamples(const int number,
670                                                                         SimpleRayContainer &rays)
671{
672  for (int i=0; i < mDistributions.size(); i++)
673        mDistributions[i]->mGeneratedRays = 0;
674
675  return SamplingStrategy::GenerateSamples(number, rays);
676}
677
678
679
680bool HwGlobalLinesDistribution::GenerateSample(SimpleRay &ray)
681{
682        Vector3 origin, termination, direction;
683
684        float radius = 0.5f *
685                Magnitude(mPreprocessor.mViewCellsManager->GetViewSpaceBox().Size());
686
687        Vector3 center = mPreprocessor.mViewCellsManager->GetViewSpaceBox().Center();
688
689        const int tries = 1000;
690        int i;
691        for (i=0; i < tries; i++)
692        {
693                float r[2];
694                sHalton.GetNext(2, r);
695
696                origin = center + (radius * UniformRandomVector(r[0], r[1]));
697                termination = center;
698               
699                if (0)
700                {
701                        // add a small offset to provide some more randomness in the sampling
702                        Vector3 offset(Random(radius * 1e-3f),
703                                                   Random(radius * 1e-3f),
704                                                   Random(radius * 1e-3f));
705                        termination += offset;
706                }
707
708                direction = termination - origin;
709
710                // $$ jb the pdf is yet not correct for all sampling methods!
711                const float c = Magnitude(direction);
712
713                if (c <= Limits::Small)
714                        return false;
715
716                direction *= 1.0f / c;
717
718                // check if the ray intersects the view space box
719                static Ray ray;
720                ray.Init(origin, direction, Ray::LOCAL_RAY);   
721
722                float tmin, tmax;
723                if (mPreprocessor.mViewCellsManager->
724                        GetViewSpaceBox().ComputeMinMaxT(ray, &tmin, &tmax) && (tmin < tmax))
725                        break;
726        }
727
728        if (i != tries)
729        {
730                // $$ jb the pdf is yet not correct for all sampling methods!
731                const float pdf = 1.0f;
732
733                ray = SimpleRay(origin, direction, HW_GLOBAL_LINES_DISTRIBUTION, pdf);
734                ray.mType = Ray::GLOBAL_RAY;
735                return true;
736        }
737
738        return false;
739}
740
741
742bool ViewCellBasedDistribution::GenerateSample(SimpleRay &ray)
743{
744        Vector3 origin, direction;
745
746        ViewCellContainer &viewCells =
747                mPreprocessor.mViewCellsManager->GetViewCells();
748
749        Vector3 point;
750        Vector3 normal;
751               
752        //Vector normalObj;
753        // float r[1];
754        // sHalton.GetNext(1, r);
755        // const int objIdx = (int)(r[0] * (float)mPreprocessor.mObjects.size() - 1.0f);
756        // Intersectable *object = mPreprocessor.mObjects[objIdx];
757        // object->GetRandomSurfacePoint(point, normal);
758        // cout << "obj: " << objIdx << endl;
759
760        float r[2];
761        sHalton.GetNext(2, r);
762
763        direction = UniformRandomVector(r[0], r[1]);
764        const float c = Magnitude(direction);
765
766    if (c <= Limits::Small)
767                return false;
768
769        direction *= 1.0f / c;
770
771        // get point on view cell surface
772        mViewCell->GetRandomSurfacePoint(origin, normal);
773
774        //direction = point - origin;
775
776        // move a little bit back to avoid piercing through walls
777        // that bound the view cell
778        origin -= 0.01f * normal;
779
780        // $$ jb the pdf is yet not correct for all sampling methods!
781        const float pdf = 1.0f;
782
783        ray = SimpleRay(origin, direction, VIEWCELL_BASED_DISTRIBUTION, pdf);
784
785        //cout << "ray: " << ray.mOrigin << " " << ray.mDirection << endl;
786
787        return true;
788}
789
790
791}
792
793
Note: See TracBrowser for help on using the repository browser.