text: /*
      This file is part of Mitsuba, a physically based rendering system.
  
      Copyright (c) 2007-2014 by Wenzel Jakob and others.
  
      Mitsuba is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License Version 3
      as published by the Free Software Foundation.
  
      Mitsuba is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      GNU General Public License for more details.
  
      You should have received a copy of the GNU General Public License
      along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
  
  #include <mitsuba/render/scene.h>
  #include <mitsuba/core/warp.h>
  
  MTS_NAMESPACE_BEGIN
  
  /*!\plugin{directional}{Directional emitter}
   * \icon{emitter_directional}
   * \order{4}
   * \parameters{
   *     \parameter{toWorld}{\Transform\Or\Animation}{
   *	      Specifies an optional emitter-to-world transformation.
   *        \default{none (i.e. emitter space $=$ world space)}
   *     }
   *     \parameter{direction}{\Vector}{
   *        Alternative to \code{toWorld}: explicitly specifies
   *        the illumination direction. Note that only one of the
   *        two parameters can be used.
   *     }
   *     \parameter{irradiance}{\Spectrum}{
   *         Specifies the amount of power per unit area received
   *         by a hypothetical surface normal to the specified direction
   *         \default{1}
   *     }
   *     \parameter{samplingWeight}{\Float}{
   *         Specifies the relative amount of samples
   *         allocated to this emitter. \default{1}
   *     }
   * }
   *
   * This emitter plugin implements a distant directional source, which
   * radiates a specified power per unit area along a fixed direction.
   * By default, the emitter radiates in the direction of the postive Z axis.
   */
  
  class DirectionalEmitter : public Emitter {
  public:
  	DirectionalEmitter(const Properties &props) : Emitter(props) {
  		m_type |= EDeltaDirection;
  
  		m_normalIrradiance = props.getSpectrum("irradiance", Spectrum::getD65());
  		if (props.hasProperty("direction")) {
  			if (props.hasProperty("toWorld"))
  				Log(EError, "Only one of the parameters 'direction' and 'toWorld'"
  					"can be used at a time!");
  
  			Vector d(normalize(props.getVector("direction"))), u, unused;
  			coordinateSystem(d, u, unused);
  			m_worldTransform = new AnimatedTransform(
  				Transform::lookAt(Point(0.0f), Point(d), u));
  		} else {
  			if (props.getTransform("toWorld", Transform()).hasScale())
  				Log(EError, "Scale factors in the emitter-to-world "
  					"transformation are not allowed!");
  		}
  	}
  
  	DirectionalEmitter(Stream *stream, InstanceManager *manager)
  	 : Emitter(stream, manager) {
  		m_normalIrradiance = Spectrum(stream);
  		m_bsphere = BSphere(stream);
  		configure();
  	}
  
  	void serialize(Stream *stream, InstanceManager *manager) const {
  		Emitter::serialize(stream, manager);
  		m_normalIrradiance.serialize(stream);
  		m_bsphere.serialize(stream);
  	}
  
  	ref<Shape> createShape(const Scene *scene) {
  		/* Create a bounding sphere that surrounds the scene */
  		m_bsphere = scene->getKDTree()->getAABB().getBSphere();
  		m_bsphere.radius *= 1.1f;
  		configure();
  		return NULL;
  	}
  
  	void configure() {
  		Emitter::configure();
  		Float surfaceArea = M_PI * m_bsphere.radius * m_bsphere.radius;
  		m_invSurfaceArea = 1.0f / surfaceArea;
  		m_power = m_normalIrradiance * surfaceArea;
  	}
  
  	Spectrum samplePosition(PositionSamplingRecord &pRec, const Point2 &sample, const Point2 *extra) const {
  		const Transform &trafo = m_worldTransform->eval(pRec.time);
  
  		Point2 p = warp::squareToUniformDiskConcentric(sample);
  
  		Vector perpOffset = trafo(Vector(p.x, p.y, 0) * m_bsphere.radius);
  		Vector d = trafo(Vector(0, 0, 1));
  
  		pRec.p = m_bsphere.center - d*m_bsphere.radius + perpOffset;
  		pRec.n = d;
  		pRec.pdf = m_invSurfaceArea;
  		pRec.measure = EArea;
  		return m_power;
  	}
  
  	Spectrum evalPosition(const PositionSamplingRecord &pRec) const {
  		return (pRec.measure == EArea) ? m_normalIrradiance : Spectrum(0.0f);
  	}
  
  	Float pdfPosition(const PositionSamplingRecord &pRec) const {
  		return (pRec.measure == EArea) ? m_invSurfaceArea : 0.0f;
  	}
  
  	Spectrum sampleDirection(DirectionSamplingRecord &dRec,
  			PositionSamplingRecord &pRec,
  			const Point2 &sample, const Point2 *extra) const {
  		dRec.d = pRec.n;
  		dRec.pdf = 1.0f;
  		dRec.measure = EDiscrete;
  		return Spectrum(1.0f);
  	}
  
  	Float pdfDirection(const DirectionSamplingRecord &dRec,
  			const PositionSamplingRecord &pRec) const {
  		return (dRec.measure == EDiscrete) ? 1.0f : 0.0f;
  	}
  
  	Spectrum evalDirection(const DirectionSamplingRecord &dRec,
  			const PositionSamplingRecord &pRec) const {
  		return Spectrum((dRec.measure == EDiscrete) ? 1.0f : 0.0f);
  	}
  
  	Spectrum sampleRay(Ray &ray,
  			const Point2 &spatialSample,
  			const Point2 &directionalSample,
  			Float time) const {
  		const Transform &trafo = m_worldTransform->eval(time);
  		Point2 p = warp::squareToUniformDiskConcentric(spatialSample);
  		Vector perpOffset = trafo(Vector(p.x, p.y, 0) * m_bsphere.radius);
  		Vector d = trafo(Vector(0, 0, 1));
  		ray.setOrigin(m_bsphere.center - d*m_bsphere.radius + perpOffset);
  		ray.setDirection(d);
  		ray.setTime(time);
  		return m_power;
  	}
  
  	Spectrum sampleDirect(DirectSamplingRecord &dRec, const Point2 &sample) const {
  		const Transform &trafo = m_worldTransform->eval(dRec.time);
  		Vector d = trafo(Vector(0,0,1));
  		Point diskCenter = m_bsphere.center - d*m_bsphere.radius;
  
  		Float distance = dot(dRec.ref - diskCenter, d);
  		if (distance < 0) {
  			/* This can happen when doing bidirectional renderings
  			   involving environment maps and directional sources. Just
  			   return zero */
  			return Spectrum(0.0f);
  		}
  
  		dRec.p = dRec.ref - distance * d;
  		dRec.d = -d;
  		dRec.n = Normal(d);
  		dRec.dist = distance;
  
  		dRec.pdf = 1.0f;
  		dRec.measure = EDiscrete;
  		return m_normalIrradiance;
  	}
  
  	Float pdfDirect(const DirectSamplingRecord &dRec) const {
  		return dRec.measure == EDiscrete ? 1.0f : 0.0f;
  	}
  
  	AABB getAABB() const {
  		return AABB();
  	}
  
  	Shader *createShader(Renderer *renderer) const;
  
  	std::string toString() const {
  		std::ostringstream oss;
  		oss << "DirectionalEmitter[" << endl
  			<< "  normalIrradiance = " << m_normalIrradiance.toString() << "," << endl
  			<< "  samplingWeight = " << m_samplingWeight << "," << endl
  			<< "  worldTransform = " << indent(m_worldTransform.toString()) << "," << endl
  			<< "  medium = " << indent(m_medium.toString()) << endl
  			<< "]";
  		return oss.str();
  	}
  
  	MTS_DECLARE_CLASS()
  private:
  	Spectrum m_normalIrradiance, m_power;
  	BSphere m_bsphere;
  	Float m_invSurfaceArea;
  };
  
  // ================ Hardware shader implementation ================
  
  class DirectionalEmitterShader : public Shader {
  public:
  	DirectionalEmitterShader(Renderer *renderer)
  		: Shader(renderer, EEmitterShader) { }
  
  	void generateCode(std::ostringstream &oss, const std::string &evalName,
  			const std::vector<std::string> &depNames) const {
  		oss << "vec3 " << evalName << "_dir(vec3 wo) {" << endl
  			<< "    return vec3(1.0);" << endl
  			<< "}" << endl;
  	}
  
  	MTS_DECLARE_CLASS()
  };
  
  Shader *DirectionalEmitter::createShader(Renderer *renderer) const {
  	return new DirectionalEmitterShader(renderer);
  }
  
  MTS_IMPLEMENT_CLASS(DirectionalEmitterShader, false, Shader)
  MTS_IMPLEMENT_CLASS_S(DirectionalEmitter, false, Emitter)
  MTS_EXPORT_PLUGIN(DirectionalEmitter, "Directional emitter");
  MTS_NAMESPACE_END
  