/* 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 . */ #include #include #include #include #include #include #include #include #include MTS_NAMESPACE_BEGIN /*!\plugin{disk}{Disk intersection primitive} * \order{4} * \parameters{ * \parameter{toWorld}{\Transform\Or\Animation}{ * Specifies a linear object-to-world transformation. * Note that non-uniform scales are not permitted! * \default{none (i.e. object space $=$ world space)} * } * \parameter{flipNormals}{\Boolean}{ * Is the disk inverted, i.e. should the normal vectors * be flipped? \default{\code{false}} * } * \vspace{-8mm} * } * \renderings{ * \rendering{Rendering with an disk emitter and a textured disk, showing * the default parameterization. (\lstref{disk})}{shape_disk} * } * * \vspace{-1mm} * This shape plugin describes a simple disk intersection primitive. It is * usually preferable over discrete approximations made from triangles. * * By default, the disk has unit radius and is located at the origin. Its * surface normal points into the positive $Z$ direction. * To change the disk scale, rotation, or translation, use the * \code{toWorld} parameter. * * \begin{xml}[caption={A simple example involving two disk instances}, label=lst:disk] * * * * * * * * * * * * * * * * * * * * \end{xml} */ class Disk : public Shape { public: Disk(const Properties &props) : Shape(props) { m_objectToWorld = new AnimatedTransform(props.getAnimatedTransform("toWorld", Transform())); if (props.getBoolean("flipNormals", false)) m_objectToWorld->prependScale(Vector(1, 1, -1)); } Disk(Stream *stream, InstanceManager *manager) : Shape(stream, manager) { m_objectToWorld = new AnimatedTransform(stream); configure(); } void serialize(Stream *stream, InstanceManager *manager) const { Shape::serialize(stream, manager); m_objectToWorld->serialize(stream); } void configure() { Shape::configure(); const Transform &trafo = m_objectToWorld->eval(0); Vector dpdu = trafo(Vector(1, 0, 0)); Vector dpdv = trafo(Vector(0, 1, 0)); if (std::abs(dot(normalize(dpdu), normalize(dpdv))) > 1e-3f) Log(EError, "Error: 'toWorld' transformation contains shear!"); if (std::abs(dpdu.length() / dpdv.length() - 1) > 1e-3f) Log(EError, "Error: 'toWorld' transformation contains a non-uniform scale!"); m_invSurfaceArea = 1.0f / (M_PI * dpdu.length() * dpdu.length()); } AABB getAABB() const { std::set times; m_objectToWorld->collectKeyframes(times); AABB aabb; for (std::set::iterator it = times.begin(); it != times.end(); ++it) { const Transform &trafo = m_objectToWorld->eval(*it); aabb.expandBy(trafo(Point( 1, 0, 0))); aabb.expandBy(trafo(Point(-1, 0, 0))); aabb.expandBy(trafo(Point( 0, 1, 0))); aabb.expandBy(trafo(Point( 0, -1, 0))); } return aabb; } Float getSurfaceArea() const { const Transform &trafo = m_objectToWorld->eval(0); Vector dpdu = trafo(Vector(1, 0, 0)); Vector dpdv = trafo(Vector(0, 1, 0)); return M_PI * dpdu.length() * dpdv.length(); } inline bool rayIntersect(const Ray &_ray, Float mint, Float maxt, Float &t, void *temp) const { Ray ray; m_objectToWorld->eval(ray.time).inverse().transformAffine(_ray, ray); Float hit = -ray.o.z / ray.d.z; if (!(hit >= mint && hit <= maxt)) return false; Point local = ray(hit); if (local.x * local.x + local.y * local.y <= 1) { t = hit; if (temp) { Float *data = static_cast(temp); data[0] = local.x; data[1] = local.y; } return true; } else { return false; } } bool rayIntersect(const Ray &ray, Float mint, Float maxt) const { Float t; return Disk::rayIntersect(ray, mint, maxt, t, NULL); } void fillIntersectionRecord(const Ray &ray, const void *temp, Intersection &its) const { const Float *data = static_cast(temp); Float r = std::sqrt(data[0] * data[0] + data[1] * data[1]), invR = (r == 0) ? 0.0f : (1.0f / r); Float phi = std::atan2(data[1], data[0]); if (phi < 0) phi += 2*M_PI; Float cosPhi = data[0] * invR, sinPhi = data[1] * invR; const Transform &trafo = m_objectToWorld->eval(ray.time); its.shape = this; if (r != 0) { its.dpdu = trafo(Vector(cosPhi, sinPhi, 0)); its.dpdv = trafo(Vector(-sinPhi, cosPhi, 0)); } else { its.dpdu = trafo(Vector(1, 0, 0)); its.dpdv = trafo(Vector(0, 1, 0)); } its.shFrame.n = normalize(trafo(Normal(0, 0, 1))); its.uv = Point2(r, phi * INV_TWOPI); its.p = ray(its.t); its.hasUVPartials = false; its.instance = NULL; its.time = ray.time; } ref createTriMesh() { const uint32_t phiSteps = 40; ref mesh = new TriMesh(getName(), phiSteps-1, 2*phiSteps, true, true, false); Point *vertices = mesh->getVertexPositions(); Normal *normals = mesh->getVertexNormals(); Point2 *texcoords = mesh->getVertexTexcoords(); Triangle *triangles = mesh->getTriangles(); Float dphi = (2 * M_PI) / (Float) (phiSteps-1); const Transform &trafo = m_objectToWorld->eval(0.0f); Point center = trafo(Point(0.0f)); Normal normal = normalize(trafo(Normal(0, 0, 1))); for (uint32_t i=0; icopyAttachments(this); mesh->configure(); return mesh.get(); } void getNormalDerivative(const Intersection &its, Vector &dndu, Vector &dndv, bool shadingFrame) const { dndu = dndv = Vector(0.0f); } void samplePosition(PositionSamplingRecord &pRec, const Point2 &sample) const { const Transform &trafo = m_objectToWorld->eval(pRec.time); Point2 p = warp::squareToUniformDiskConcentric(sample); pRec.p = trafo(Point3(p.x, p.y, 0)); pRec.n = trafo(normalize(Normal(0,0,1))); pRec.pdf = m_invSurfaceArea; pRec.measure = EArea; } Float pdfPosition(const PositionSamplingRecord &pRec) const { return m_invSurfaceArea; } size_t getPrimitiveCount() const { return 1; } size_t getEffectivePrimitiveCount() const { return 1; } std::string toString() const { std::ostringstream oss; oss << "Disk[" << endl << " objectToWorld = " << indent(m_objectToWorld->toString()) << "," << endl << " bsdf = " << indent(m_bsdf.toString()) << "," << endl; if (isMediumTransition()) { oss << " interiorMedium = " << indent(m_interiorMedium.toString()) << "," << endl << " exteriorMedium = " << indent(m_exteriorMedium.toString()) << "," << endl; } oss << " emitter = " << indent(m_emitter.toString()) << "," << endl << " sensor = " << indent(m_sensor.toString()) << "," << endl << " subsurface = " << indent(m_subsurface.toString()) << endl << "]"; return oss.str(); } MTS_DECLARE_CLASS() private: ref m_objectToWorld; Float m_invSurfaceArea; }; MTS_IMPLEMENT_CLASS_S(Disk, false, Shape) MTS_EXPORT_PLUGIN(Disk, "Disk intersection primitive"); MTS_NAMESPACE_END