Exemplo n.º 1
0
void PhotonQuery::push_back(Photon const &data,
                            Photon::vector_type const &vDistance) {
  tgir::Real const rCosTheta =
      -hi::dot(data.dir(), pathVertex_.vGeometricNormal);
  if (rCosTheta <= 0) {
    return;
  }

  tgir::Vector3 const vPoint =
      vDistance + data.dir() * hi::dot(vDistance, pathVertex_.vGeometricNormal);
  tgir::Real const rDistanceSquared = hi::length_squared(vPoint);
  if (rDistanceSquared >= PhotonQuery::max_search_range()) {
    return;
  }
#if 0  // フィルタをかけるか
#if 1  // コーンフィルタか
    static tgir::Real const k = 1.1; // 円錐フィルタのパラメータ
    tgir::Real const w = 1 - std::sqrt(rDistanceSquared) / (k * PhotonQuery::max_distance());
#else
    static tgir::Real const alpha = 0.918; // ガウスフィルタのパラメータ
    static tgir::Real const beta = 1.953;
    tgir::Real const u = 1 - std::exp(-beta * rDistanceSquared / (2 * PhotonQuery::max_search_range()));
    tgir::Real const s = 1 - std::exp(-beta);
    tgir::Real const w = alpha * (1 - u / s);
#endif
    rPower_ += data.pow() * w;
#else
  rPower_ += data.pow();
#endif
}
Exemplo n.º 2
0
Arquivo: Photon.C Projeto: gyoto/Gyoto
Photon::Photon(const Photon& o) :
  Worldline(o), SmartPointee(o),
  object_(NULL),
  freq_obs_(o.freq_obs_), transmission_freqobs_(o.transmission_freqobs_),
  spectro_(NULL), transmission_(NULL)
{
  if (o.object_()) {
    object_  = o.object_  -> clone();
    object_ -> metric(metric_);
  }
  if (o.spectro_()) {
    spectro_ = o.spectro_ -> clone();
    _allocateTransmission();
    if (size_t nsamples = spectro_->nSamples())
      memcpy(transmission_, o.getTransmission(), nsamples*sizeof(double));
  }
}
Exemplo n.º 3
0
PhotonMap* PhotonMap::PrecomputeIrradiance(int increment, const int N, const float maxDistance)
{
    std::cout << "Precomputing irradiance" << std::endl;

    PhotonMap *precomputed = new PhotonMap(MAX_PHOTON_COUNT);

    for(int i = 0; i < (int)m_photons.size(); i += increment)
    {
        Photon p = m_photons[i];
        p.Power(GetRadianceEstimate(N, maxDistance, m_photons[i].Position(), m_photons[i].SurfNormal()));

        precomputed->AddPhoton(p);
    }

    precomputed->BalanceAndConstruct();

    return precomputed;
}
Exemplo n.º 4
0
void PhotonMap::Map::PrecomputeIrradianceEstimates(
	unsigned int numThreads,
	unsigned int threadNum,
	float searchRadius,
	unsigned int searchCount
) {
	assert(finalized);

	if (numPhotons > 0) {
		static boost::mutex progressMutex;

		const unsigned int photonsPerThread = numPhotons / numThreads;
		const unsigned int photonsRemaining = numPhotons % photonsPerThread;

		const unsigned int photonIdxL = (photonsPerThread * threadNum) + 1;
		const unsigned int photonIdxR =
			(photonsPerThread * (threadNum + 1)) +
			((threadNum == (numThreads - 1))? photonsRemaining: 0);
		const float photonRange = photonIdxR - photonIdxL;

		unsigned int prevProgress = 0;
		unsigned int currProgress = 0;

		for (unsigned int photonIdx = photonIdxL; photonIdx <= photonIdxR; photonIdx++) {
			Photon* p = &photonArray[photonIdx];
				p->SetIrr(GetIrradianceEstimate(p->GetPos(), p->GetNrm(), searchRadius, searchCount, true));

			currProgress = ((photonIdx - photonIdxL) / photonRange) * 100;

			if ((currProgress > prevProgress) && ((currProgress % 10) == 0)) {
				prevProgress = currProgress;

				boost::mutex::scoped_lock lock(progressMutex);

				std::cout << "[PhotonMap::Map::PrecomputeIrradiance]";
				std::cout << " thread: " << threadNum << ", progress: " << currProgress << "%";
				std::cout << " (photon " << (photonIdx - photonIdxL) << " of " << (photonIdxR - photonIdxL + 1) << ")";
				std::cout << std::endl;
			}
		}
	}
}
Exemplo n.º 5
0
void
unittest::LineOnEllipseIntersection(){
    
    Point3D centre(13,5,0);
    double a = 3;
    double b = 2;
    
    ellipse e(centre, a, b);
    
    Point3D position(1,3.005,0);
    Vector3D momentum(1,0,0);
    
    Photon testphoton;
    testphoton.SetPosition(position);
    testphoton.SetMomentum(momentum);
    
    e.LineOnEllipseIntersection(testphoton);
    
    
}
Exemplo n.º 6
0
void
unittest::Ellipse_Points3D(){
    
    Point3D centre(13,5,0);
    double a = 3;
    double b = 2;
    
    ellipse e(centre, a, b);
    
    Point3D position(2,2,0);
    Vector3D momentum(11,3,-1);
    
    Photon testphoton;
    testphoton.SetPosition(position);
    testphoton.SetMomentum(momentum);
    
    e.points3D(testphoton);
    
    Test reader;
    
    reader.PrintPoint(e.GetStorage().GetPoint());
    reader.PrintPoint(e.GetStorage().GetPoint2());
    
}
Exemplo n.º 7
0
void PhotonMapProperty::operator()(Photon const &photon) {
  ++nPhotons_;

  tgir::Real const rPower = photon.pow();

  rPowerAvg_ += rPower;
  rPowerVar_ += hi::square_of(rPower);

  if (rPowerMin_ > rPower) {
    rPowerMin_ = rPower;
  }

  if (rPowerMax_ < rPower) {
    rPowerMax_ = rPower;
  }
}
Exemplo n.º 8
0
void Surface::stop_photon(Photon& p)
{
    if (p.is_valid()==false)
        return;

    if(isinf(_dCurvature))
    {
        p.valid=false;
        return;
    }

    // flat case (intersect with z=0)
    if (p.dz==0.)
    {
        // TODO
        p.valid=false;
        return;
    }

    double tmin=-p.z/p.dz;
    if(_bIsFlat && (tmin<0.) )
    {
        // in the past of the photon
        p.valid=false;
        return;
    }

    // TODO optimise in case of spherical

    // stop the photon in z=0
    p.x+=tmin*p.dx;
    p.y+=tmin*p.dy;
    p.z=0.;

    if(_bIsFlat || _bIsPerfect)
    {
        p.valid=update_auto_diameter(p.x,p.y);
        return;
    }

    //compute the coef of the 2nd degree eq in t:
    //the equation is t^2A+tB+C=0
    // use p.z=0.
    double dA=_dCurvature*(sqr(p.dx)+sqr(p.dy)+(_dConic+1.)*sqr(p.dz));
    double dB=2.*(_dCurvature*(p.x*p.dx+p.y*p.dy)-p.dz);
    double dC=_dCurvature*(sqr(p.x)+sqr(p.y));

    double tfinal;

    if (dA==0.)
    {
        if (dB==0.)
        {
            p.valid=false;
            return;
        }

        //the equation is now : t*dB+dC=0 so:
        tfinal=-dC/dB;
    }
    else //dA!=0
    {
        //solve the equation
        double dB2=dB*dB;
        double delta=dB2-4.*dC*dA;

        if (delta<0.)
        {
            p.valid=false;
            return;
        }

        double t1,t2;

        if (delta!=dB2) // TODO enhance test
        {
            double sqrtDelta=sqrt(delta);
            t1=(2.*dC)/(+sqrtDelta-dB);
            t2=(2.*dC)/(-sqrtDelta-dB);
        }
        else
        {
            // delta~=dB2
            // use approximate solution:
            if (dB!=0.)
            {
                t1=-dC/dB;
                t2=10.*t1; // to choose  t1
            }
            else
            {
				p.valid=false;  // bug if we are here
                return;
            }
        }

        // select t that gives the lowest abs(z)
        //t1ok=abs(z+t1.*dz)<abs(z+t2.*dz); //oct ver
        if (t1*t1<t2*t2) // todo optimize
            tfinal=t1;
        else
            tfinal=t2;
    }

    // check if intersection is in the futur of the photon
    if ( tmin+tfinal<0 )
    {
        p.valid=false;
        return;
    }

    p.x+=p.dx*tfinal;
    p.y+=p.dy*tfinal;
    p.z+=p.dz*tfinal;

    assert(p.is_valid());

    p.valid=update_auto_diameter(p.x,p.y);

    if(!_bIsAspheric)
        return;

    //aspheric mode
    //p.x,p.y,p.z is already a good approximation of the surface (as a conic), but make some newton step

    double x=p.x;
    double y=p.y;
    // double z=p.z;
    double dOldT=0;
    for(int iLoop=0;iLoop<NB_ITER_STOP_NEWTON;iLoop++)
    {
        //reproject z on aspheric curve
        double zproj;
        compute_z(x,y,zproj);

        //compute normal on aspheric surface
        double nx,ny,nz;
        compute_normal(x,y,zproj,nx,ny,nz);

        //compute the d value to have the plane x*nx+y*ny+z*nz+d=0
        double d=-(x*nx+y*ny+zproj*nz);

        //compute the intersect of this plane and the line defined by p (one newton step)
        double t=(-d-p.x*nx-p.y*ny-p.z*nz)/(p.dx*nx+p.dy*ny+p.dz*nz); //TODO tester !=0
        double distSQ=sqr(t-dOldT)*(sqr(p.dx)+sqr(p.dy)+sqr(p.dz));
        x=p.x+t*p.dx;
        y=p.y+t*p.dy;
        double z=p.z+t*p.dz;

        if(distSQ<sqr(RESOLUTION_STOP_NEWTON))
        {
            p.x=x;
            p.y=y;
            p.z=z;

            p.valid=update_auto_diameter(p.x,p.y);
            return;
        }

        dOldT=t;
    }

    //too many iterations
    p.valid=false;
    return;
}
Exemplo n.º 9
0
void Surface::reflect_photon(Photon &p)
{
    if (!p.is_valid())
        return;

    stop_photon(p);
    if (!p.is_valid())
        return;

    if(_bIsPerfect)
    {
        //TODO optimise and merge tests
        if(p.dz==0.) //no intersection
        {
            p.valid=false;
            return;
        }

        //compute AP
        if( (_dCurvature<CURVATURE_FLAT) && (_dCurvature>-CURVATURE_FLAT))
        {
            p.dz=-p.dz;
            return; //flat mirror
        }

        double dFocal=0.5/_dCurvature;
        double t=dFocal/p.dz;
        double ax=p.dx*t;
        double ay=p.dy*t;
        double az=p.dz*t;

        //TODO optimise tests with sign(t)
        if(_dCurvature>0.)
        {
            if(p.dz>0.)
            {
                p.dx=+ax+p.x;
                p.dy=+ay+p.y;
                p.dz=-az-p.z;
            }
            else
            {
                p.dx=-ax-p.x;
                p.dy=-ay-p.y;
                p.dz=+az-p.z;
            }
        }
        else
        {
            if(p.dz>0.)
            {
                p.dx=-ax-p.x;
                p.dy=-ay-p.y;
                p.dz=+az-p.z;
            }
            else
            {
                p.dx=+ax+p.x;
                p.dy=+ay+p.y;
                p.dz=-az-p.z;
            }
        }

        return;
    }

    double nx,ny,nz;
    compute_normal(p.x,p.y,p.z,nx,ny,nz);

    // compute with the normal (nx,ny,nz)
    double dRayonSq=sqr(nx)+sqr(ny)+sqr(nz);
    assert(dRayonSq>0.);
    double u=2.*(nx*p.dx+ny*p.dy+nz*p.dz)/dRayonSq;
    p.dx-=nx*u;
    p.dy-=ny*u;
    p.dz-=nz*u;
}
Exemplo n.º 10
0
void Surface::transmit_photon(Photon& p)
{
    if (!p.is_valid())
        return;

    stop_photon(p);
    if (!p.is_valid())
        return;

    if(_bIsPerfect)
    {
        //TODO optimise and merge tests
        if(p.dz==0.) //no intersection
        {
            p.valid=false;
            return;
        }

        //compute AP
        if( (_dCurvature<CURVATURE_FLAT) && (_dCurvature>-CURVATURE_FLAT))
            return; //flat lens

        double dFocal=0.5/_dCurvature;
        double t=dFocal/p.dz;

        if(p.dz<0.)
            t=-t;

        double ax=p.dx*t;
        double ay=p.dy*t;
        double az=p.dz*t;

        p.dx=ax-p.x;
        p.dy=ay-p.y;
        p.dz=az-p.z;

        if(dFocal<0.)
        {
            p.dx=-p.dx;
            p.dy=-p.dy;
            p.dz=-p.dz;
        }

        return;
    }

    assert(_pMaterialNext!=0);
    assert(_pMaterialPrev!=0);

    double nx,ny,nz;

    //compute the normal in p (center distance : ro)
    double dRadiusSq=sqr(p.x)+sqr(p.y);
    if (dRadiusSq!=0.)
    {
        compute_normal(p.x,p.y,p.z,nx,ny,nz);
        Vector3D::normalize(nx,ny,nz);
    }
    else // at surface center or flat surface
    {
        nx=0.;
        ny=0.;
        nz=1.;
    }

    double u=nx*p.dx+ny*p.dy+nz*p.dz;

    // compute with the normal (dx,dy,dz)
    // u is the projection of I on the normal

    //calcule de C1=u/I
    double dC12=u*u/(sqr(p.dx)+sqr(p.dy)+sqr(p.dz));
    double dRatioN=_pMaterialNext->index(p.lambda())/_pMaterialPrev->index(p.lambda()); //TODO store index and reuse
    double denom=sqr(dRatioN)+dC12-1.;

    if (denom<=0.)
    {  // total reflexion
        p.valid=false;
        return;
    }

    double dT2T1=sqrt(dC12/denom);
    double k=(1.-dT2T1)*u;

    p.dx=nx*k+dT2T1*p.dx;
    p.dy=ny*k+dT2T1*p.dy;
    p.dz=nz*k+dT2T1*p.dz;
}
Exemplo n.º 11
0
void LaserSource::reaction(Photon& photon, std::vector<std::vector<Photon>>& lightPaths)
{
	photon.setVelocity(0.0);
}
Exemplo n.º 12
0
    void preprocess(const Scene *scene) {
        /* Create a sample generator for the preprocess step */
        Sampler *sampler = static_cast<Sampler *>(
            NoriObjectFactory::createInstance("independent", PropertyList()));

        Emitter* distantsDisk = scene->getDistantEmitter();
        if(distantsDisk != nullptr ) {
            float lngstDir = scene->getBoundingBox().getLongestDirection();
            distantsDisk->setMaxRadius(lngstDir);
        }

        /* Allocate memory for the photon map */
        m_photonMap = std::unique_ptr<PhotonMap>(new PhotonMap());
        m_photonMap->reserve(m_photonCount);

		/* Estimate a default photon radius */
		if (m_photonRadius == 0)
			m_photonRadius = scene->getBoundingBox().getExtents().norm() / 500.0f;

        int storedPhotons = 0;

        const  std::vector<Emitter *> lights = scene->getEmitters();
        int nLights = lights.size();
        Color3f tp(1.0f, 1.0f, 1.0f);

        cout << "Starting to create "<< m_photonCount << " photons!" << endl;
        int percentDone= 0;
        int onePercent = int(floor(m_photonCount / 100.0));

        // create the expected number of photons
        while(storedPhotons < m_photonCount) {
            //uniformly sample 1 light (assuming that we only have area lights)
            int var = int(std::min(sampler->next1D()*nLights, float(nLights) - 1.0f));
            const areaLight* curLight = static_cast<const areaLight *> (lights[var]);

            //sample a photon
            Photon curPhoton;
            Vector3f unQuantDir(0.0f,0.0f,0.0f);
            curLight->samplePhoton(sampler, curPhoton, 1, nLights, unQuantDir);
            Color3f alpha = curPhoton.getPower();
            Color3f tp(1.0f, 1.0f, 1.0f);


            //trace the photon
            Intersection its;
            Ray3f photonRay(curPhoton.getPosition(), unQuantDir);
            m_shootedRays++;
            if (scene->rayIntersect(photonRay, its)) {
                while(true) {
                    const BSDF* curBSDF = its.mesh->getBSDF();


                    if (curBSDF->isDiffuse()) {
                        //store the photon
                        m_photonMap->push_back(Photon(
                            its.p  /* Position */,
                            -photonRay.d /* Direction*/,
                            tp * alpha  /* Power */
                        ));
                        storedPhotons++;
                    }

                    if(!(storedPhotons < m_photonCount)) break;

                    BSDFQueryRecord query = BSDFQueryRecord(its.toLocal(-photonRay.d), Vector3f(0.0f), EMeasure::ESolidAngle);
                    Color3f fi =  curBSDF->sample(query, sampler->next2D());

                    if(fi.maxCoeff() == 0.0f) break;

                    tp *= fi;

                    Vector3f wo = its.toWorld(query.wo);
                    photonRay = Ray3f(its.p, wo);

                    //ray escapes the scene
                    if (!scene->rayIntersect(photonRay, its)) break;

                    //stop critirium russian roulette
                    float q = tp.maxCoeff();
                    if(q < sampler->next1D()) break;
                    tp /= q;
                }

            }
            if(onePercent != 0) {
                if(storedPhotons % onePercent == 0){
                    int percent = int(floor(storedPhotons / onePercent));
                    if(percent % 10 == 0 && percentDone != percent){
                        percentDone = percent;
                        cout << percent << "%" << endl;
                    }
                }
            }
        }

		/* Build the photon map */
        m_photonMap->build();
    }
Exemplo n.º 13
0
vector<Photon> selectPhotonsCMG(const Event & ev) {
	const SingleVariableContainer<int> * gn = dynamic_cast<const SingleVariableContainer<int> *>(ev.findVariable("gn"));
	const ArrayVariableContainer<float> * gn_px = dynamic_cast<const ArrayVariableContainer<float> *>(ev.findVariable("gn_px"));
	const ArrayVariableContainer<float> * gn_py = dynamic_cast<const ArrayVariableContainer<float> *>(ev.findVariable("gn_py"));
	const ArrayVariableContainer<float> * gn_pz = dynamic_cast<const ArrayVariableContainer<float> *>(ev.findVariable("gn_pz"));
	const ArrayVariableContainer<float> * gn_en = dynamic_cast<const ArrayVariableContainer<float> *>(ev.findVariable("gn_en"));
	const ArrayVariableContainer<int> * gn_idbits = dynamic_cast<const ArrayVariableContainer<int> *>(ev.findVariable("gn_idbits"));
	const ArrayVariableContainer<float> * gn_chIso03 = dynamic_cast<const ArrayVariableContainer<float> *>(ev.findVariable("gn_chIso03"));
	const ArrayVariableContainer<float> * gn_nhIso03 = dynamic_cast<const ArrayVariableContainer<float> *>(ev.findVariable("gn_nhIso03"));
	const ArrayVariableContainer<float> * gn_gIso03 = dynamic_cast<const ArrayVariableContainer<float> *>(ev.findVariable("gn_gIso03"));

	const ArrayVariableContainer<float> * egn_sceta = dynamic_cast<const ArrayVariableContainer<float> *>(ev.findVariable("egn_sceta"));
	const ArrayVariableContainer<bool> * egn_isConv = dynamic_cast<const ArrayVariableContainer<bool> *>(ev.findVariable("egn_isConv"));
	const ArrayVariableContainer<float> * egn_hoe = dynamic_cast<const ArrayVariableContainer<float> *>(ev.findVariable("egn_hoe"));
	const ArrayVariableContainer<float> * egn_sihih = dynamic_cast<const ArrayVariableContainer<float> *>(ev.findVariable("egn_sihih"));
	const ArrayVariableContainer<float> * egn_r9 = dynamic_cast<const ArrayVariableContainer<float> *>(ev.findVariable("egn_r9"));

	const ArrayVariableContainer<int> * pidC = dynamic_cast<const ArrayVariableContainer<int> *>(ev.findVariable("gn_pid"));

	vector<Photon> photons;
	for ( int i = 0; i < gn->getVal(); ++i ) {
		Photon tmpPhoton;

		tmpPhoton.addFloatVar( gn_px->getName(), gn_px->getVal(i) );
		tmpPhoton.addFloatVar( gn_py->getName(), gn_py->getVal(i) );
		tmpPhoton.addFloatVar( gn_pz->getName(), gn_pz->getVal(i) );
		tmpPhoton.addFloatVar( gn_en->getName(), gn_en->getVal(i) );
		tmpPhoton.addIntVar( gn_idbits->getName(), gn_idbits->getVal(i) );
		tmpPhoton.addFloatVar( gn_chIso03->getName(), gn_chIso03->getVal(i) );
		tmpPhoton.addFloatVar( gn_nhIso03->getName(), gn_nhIso03->getVal(i) );
		tmpPhoton.addFloatVar( gn_gIso03->getName(), gn_gIso03->getVal(i) );

		int pid = pidC->getVal(i);

		tmpPhoton.addFloatVar( egn_sceta->getName(), egn_sceta->getVal(pid) );
		tmpPhoton.addBoolVar( egn_isConv->getName(), egn_isConv->getVal(pid) );
		tmpPhoton.addFloatVar( egn_hoe->getName(), egn_hoe->getVal(pid) );
		tmpPhoton.addFloatVar( egn_sihih->getName(), egn_sihih->getVal(pid) );
		tmpPhoton.addFloatVar( egn_r9->getName(), egn_r9->getVal(pid) );

		photons.push_back(tmpPhoton);
	}
	return photons;
}
void GameScreen::calculatePath()
{
	/*
	std::map<int, std::shared_ptr<Equipment>>::iterator it_on_grid = tool_manager.equipments_on_grid_.begin();
	for(; it_on_grid!=tool_manager.equipments_on_grid_.end(); it_on_grid ++)
	{
		(*it_on_grid).second->lightOff();
	}
	*/
	for(int i = 0; i != GameScreen::tool_manager.my_targets_.size(); i++)
	{
		GameScreen::tool_manager.my_targets_[i]->lightOff();
	}
	GameScreen::tool_manager.my_targets_[0]->lightOff();
	sf::FloatRect windowRect(MARGIN, MARGIN, GRID_WIDTH*(BLOCK_SIZE), GRID_HEIGHT*(BLOCK_SIZE));
	if(lightPaths.size() == 0)
	{
		for(int i = 0; i != tool_manager.my_lasers_.size(); i++)
		{
			std::vector<Photon> lightPath;
			lightPath.push_back(tool_manager.my_lasers_[i].getPhoton());
			lightPaths.push_back(lightPath);
		}
	}
	for(int i = 0; i != lightPaths.size(); i++)
	{
		Photon current = lightPaths[i].back();
		while(current.getVelocity() != 0.0 && windowRect.contains(current.getPosition()))
		{
			Photon nextPhoton = current;
			int idx = nextPhoton.getIndex();
			if(tool_manager.equipments_on_grid_.count(idx) > 0)
			{
				tool_manager.equipments_on_grid_[idx]->reaction(nextPhoton, lightPaths);
				lightPaths[i].push_back(nextPhoton);
			}
			else
			{
				nextPhoton.myMove();
				lightPaths[i].push_back(nextPhoton);
			}
			current = nextPhoton;
		}
	}
	bool isAllHit = true;
	/*
	it_on_grid = tool_manager.equipments_on_grid_.begin();
	for(; it_on_grid!=tool_manager.equipments_on_grid_.end(); it_on_grid ++)
	{
		if(!(*it_on_grid).second->isHit())
		{
			isAllHit = false;
			break;
		}
	}
	*/
	for(int i = 0; i != GameScreen::tool_manager.my_targets_.size(); i++)
	{
		if(!GameScreen::tool_manager.my_targets_[0]->isHit())
		{
			isAllHit = false;
			break;
		}
	}
	if(isAllHit)
	{
		std::cout<<"all hit"<<std::endl;
	}
}
void MCMLModel::DoOneRun (long numPhotonsSet) {
  Photon photon;
  for (long i = 0; i < numPhotonsSet; i++) {
    photon.RunOnePhoton(this);
  }
}
// ======================================================================
// During ray tracing, when a diffuse (or partially diffuse) object is
// hit, gather the nearby photons to approximate indirect illumination
bool comparePhotons(const Photon& p1,const Photon& p2)
{
	double dis1 = (p1.getPosition()-p1.getPoint()).Length();
	double dis2 = (p2.getPosition()-p2.getPoint()).Length();
	return dis1<dis2;
}
Exemplo n.º 17
0
// combines "tauint" (used to find the location of the
// forced first scattering event) and "tauint2" (used
// to find subsequent scattering positions within the
// grid from RANDOM optical depths)
bool Grid::Integrate(Photon& p, cfloat tau, IntegrateMode im) const {
	bool ret = true;

	float taurun = 0.0f, taucell = 0.0f;
	float dtmp = 0.0f, dpath = 0.0f, dcell = 0.0f;

	// translate by half the grid's represented dimensions
	// (so pcur corresponds to the photon cell indices)
	vec3 pcur = p.pos + hsize;

	int celli = p.xcell;
	int cellj = p.ycell;
	int cellk = p.zcell;


	#ifdef DEBUG
	assert(IndicesInBounds(celli, cellj, cellk));
	#endif


	cfloat dsx = GetMaxXDistance(p, pcur); float dx = 0.0f;
	cfloat dsy = GetMaxYDistance(p, pcur); float dy = 0.0f;
	cfloat dsz = GetMaxZDistance(p, pcur); float dz = 0.0f;

	cfloat dmax = std::min(std::min(dsx, dsy), dsz);

	if (dmax < .001f) {
		return false;
	}


	// start the path integration
	while (taurun < tau && dpath < dmax * 0.999f) {
		if (p.dir.x > 0.0f) {
			dx = (xfaces[celli + 1] - pcur.x) / p.dir.x;

			if (dx < EPSILON) {
				pcur.x = xfaces[celli + 1];
				celli += 1;
				dx = (xfaces[celli + 1] - pcur.x) / p.dir.x;
			}
		} else if (p.dir.x < 0.0f) {
			dx = (xfaces[celli] - pcur.x) / p.dir.x;

			if (dx < EPSILON) {
				pcur.x = xfaces[celli];
				dx = (xfaces[celli - 1] - pcur.x) / p.dir.x;
				celli -= 1;
			}
		} else if (p.dir.x == 0.0f) {
			dx = hsize.x * 10000.0f;
		}

		if (p.dir.y > 0.0f) {
			dy = (yfaces[cellj + 1] - pcur.y) / p.dir.y;

			if (dy < EPSILON) {
				pcur.y = yfaces[cellj + 1];
				cellj += 1;
				dy = (yfaces[cellj + 1] - pcur.y) / p.dir.y;
			}
		} else if (p.dir.y < 0.0f) {
			dy = (yfaces[cellj] - pcur.y) / p.dir.y;

			if (dy < EPSILON) {
				pcur.y = yfaces[cellj];
				dy = (yfaces[cellj - 1] - pcur.y) / p.dir.y;
				cellj -= 1;
			}
		} else if (p.dir.y == 0.0f) {
			dy = hsize.y * 10000.0f;
		}

		if (p.dir.z > 0.0f) {
			dz = (zfaces[cellk + 1] - pcur.z) / p.dir.z;

			if (dz < EPSILON) {
				pcur.z = zfaces[cellk + 1];
				cellk += 1;
				dz = (zfaces[cellk + 1] - pcur.z) / p.dir.z;
			}
		} else if (p.dir.z < 0.0f) {
			dz = (zfaces[cellk] - pcur.z) / p.dir.z;

			if (dz < EPSILON) {
				pcur.z = zfaces[cellk];
				dz = (zfaces[cellk - 1] - pcur.z) / p.dir.z;
				cellk -= 1;
			}
		} else if (p.dir.z == 0.0f) {
			dz = hsize.z * 10000.0f;
		}

		// distances can be zero if we
		// are right on a cell boundary
		if (dx == 0.0f || fabs(dx) < .001f) { dx = hsize.x * 10000.0f; }
		if (dy == 0.0f || fabs(dy) < .001f) { dy = hsize.y * 10000.0f; }
		if (dz == 0.0f || fabs(dz) < .001f) { dz = hsize.z * 10000.0f; }

		// find distance to next cell wall
		// (minimum value of dx, dy, and dz)
		dcell = std::min(std::min(dx, dy), dz);

		if (dx < 0.0f) { dcell = std::min(dy, dz); }
		if (dy < 0.0f) { dcell = std::min(dx, dz); }
		if (dz < 0.0f) { dcell = std::min(dx, dy); }

		// if moving through a "thicker" region of the grid
		// where the cells are denser, we reach tau quicker
		taucell = dcell * rhokappa[celli][cellj][cellk];

		// (if taurun + taucell > tau) then the photon's next
		// interaction event will be at distance d + dtmp, so
		// update photon position and cell indices; otherwise
		// photon moves over a distance dcell to the next cell
		// wall
		if (taurun + taucell >= tau) {
			dtmp = (tau - taurun) / rhokappa[celli][cellj][cellk];
			dpath += dtmp;
			taurun += taucell;

			pcur += (p.dir * dtmp);
		} else {
			dpath += dcell;
			taurun += taucell;

			pcur += (p.dir * dcell);
		}


		if (!GPosInBounds(pcur)) {
			// fight numerical inaccuracy
			ClampGPos(pcur);
		}


		celli = GetXCellIdx(pcur.x - hsize.x); // GetXCellIdx() already adds hsize.x to x!
		cellj = GetYCellIdx(pcur.y - hsize.y); // GetYCellIdx() already adds hsize.y to y!
		cellk = GetZCellIdx(pcur.z - hsize.z); // GetZCellIdx() already adds hsize.z to z!


		#ifdef DEBUG
		assert(IndicesInBounds(celli, cellj, cellk));
		#endif
	}


	switch (im) {
		case IM_RANDOM: {
			if (dpath >= dmax * 0.999f) {
				// photon escapes grid
				ret = false;
			} else {
				// photon stays inside, update
				// its pos and cell indices to
				// the next interaction coords
				p.UpdatePos(dpath);

				if (!RPosInBounds(p.pos)) {
					// fight numerical inaccuracy
					ClampRPos(p.pos);
				}

				p.UpdateCellIndices(*this);
			}
		} break;

		case IM_FORCED: {
			p.pos = pcur - hsize;

			if (!RPosInBounds(p.pos)) {
				// fight numerical inaccuracy
				ClampRPos(p.pos);
			}

			p.UpdateCellIndices(*this);
		} break;
	}

	return ret;
}