/* 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 MTS_NAMESPACE_BEGIN /*!\plugin{rectangle}{Rectangle intersection primitive} * \order{3} * \parameters{ * \parameter{toWorld}{\Transform\Or\Animation}{ * Specifies a linear object-to-world transformation. * It is allowed to use non-uniform scaling, but no shear. * \default{none (i.e. object space $=$ world space)} * } * \parameter{flipNormals}{\Boolean}{ * Is the rectangle inverted, i.e. should the normal vectors * be flipped? \default{\code{false}} * } * } * \renderings{ * \rendering{Two rectangles configured as a reflective surface and an * emitter (\lstref{rectangle})}{shape_rectangle} * } * * This shape plugin describes a simple rectangular intersection primitive. * It is mainly provided as a convenience for those cases when creating and * loading an external mesh with two triangles is simply too tedious, e.g. * when an area light source or a simple ground plane are needed. * * By default, the rectangle covers the XY-range $[-1,1]\times[-1,1]$ * and has a surface normal that points into the positive $Z$ direction. * To change the rectangle scale, rotation, or translation, use the * \code{toWorld} parameter. * * \vspace{2mm} * \begin{xml}[caption={A simple example involving two rectangle instances}, label=lst:rectangle] * * * * * * * * * * * * * * * * * \end{xml} */ class Rectangle : public Shape { public: Rectangle(const Properties &props) : Shape(props) { m_objectToWorld = props.getTransform("toWorld", Transform()); if (props.getBoolean("flipNormals", false)) m_objectToWorld = m_objectToWorld * Transform::scale(Vector(1, 1, -1)); m_worldToObject = m_objectToWorld.inverse(); } Rectangle(Stream *stream, InstanceManager *manager) : Shape(stream, manager) { m_objectToWorld = Transform(stream); m_worldToObject = m_objectToWorld.inverse(); configure(); } void serialize(Stream *stream, InstanceManager *manager) const { Shape::serialize(stream, manager); m_objectToWorld.serialize(stream); } void configure() { Shape::configure(); m_dpdu = m_objectToWorld(Vector(2, 0, 0)); m_dpdv = m_objectToWorld(Vector(0, 2, 0)); Normal normal = normalize(m_objectToWorld(Normal(0, 0, 1))); m_frame = Frame(normalize(m_dpdu), normalize(m_dpdv), normal); m_invSurfaceArea = 1.0f / getSurfaceArea(); if (std::abs(dot(normalize(m_dpdu), normalize(m_dpdv))) > 1e-3f) Log(EWarn, "Error: 'toWorld' transformation contains shear!"); } AABB getAABB() const { AABB aabb; aabb.expandBy(m_objectToWorld(Point(-1, -1, 0))); aabb.expandBy(m_objectToWorld(Point( 1, -1, 0))); aabb.expandBy(m_objectToWorld(Point( 1, 1, 0))); aabb.expandBy(m_objectToWorld(Point(-1, 1, 0))); return aabb; } Float getSurfaceArea() const { return m_dpdu.length() * m_dpdv.length(); } inline bool rayIntersect(const Ray &_ray, Float mint, Float maxt, Float &t, void *temp) const { Ray ray; m_worldToObject.transformAffine(_ray, ray); Float hit = -ray.o.z / ray.d.z; if (!(hit >= mint && hit <= maxt)) return false; Point local = ray(hit); if (std::abs(local.x) <= 1 && std::abs(local.y) <= 1) { t = hit; if (temp) { Float *data = static_cast(temp); data[0] = local.x; data[1] = local.y; } return true; } return false; } bool rayIntersect(const Ray &ray, Float mint, Float maxt) const { Float t; return Rectangle::rayIntersect(ray, mint, maxt, t, NULL); } void fillIntersectionRecord(const Ray &ray, const void *temp, Intersection &its) const { const Float *data = static_cast(temp); its.geoFrame = m_frame; its.shFrame = its.geoFrame.n; its.shape = this; its.dpdu = m_dpdu; its.dpdv = m_dpdv; its.uv = Point2(0.5f * (data[0]+1), 0.5f * (data[1]+1)); its.p = ray(its.t); its.hasUVPartials = false; its.instance = NULL; its.time = ray.time; } ref createTriMesh() { ref mesh = new TriMesh(getName(), 2, 4, true, true, false); Point *vertices = mesh->getVertexPositions(); Normal *normals = mesh->getVertexNormals(); Point2 *texcoords = mesh->getVertexTexcoords(); Triangle *triangles = mesh->getTriangles(); vertices[0] = m_objectToWorld(Point(-1, -1, 0)); vertices[1] = m_objectToWorld(Point( 1, -1, 0)); vertices[2] = m_objectToWorld(Point( 1, 1, 0)); vertices[3] = m_objectToWorld(Point(-1, 1, 0)); texcoords[0] = Point2(0, 0); texcoords[1] = Point2(1, 0); texcoords[2] = Point2(1, 1); texcoords[3] = Point2(0, 1); normals[0] = normals[1] = normals[2] = normals[3] = m_frame.n; triangles[0].idx[0] = 0; triangles[0].idx[1] = 1; triangles[0].idx[2] = 2; triangles[1].idx[0] = 2; triangles[1].idx[1] = 3; triangles[1].idx[2] = 0; mesh->setBSDF(m_bsdf); mesh->setEmitter(m_emitter); 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 { pRec.p = m_objectToWorld(Point3(sample.x * 2 - 1, sample.y * 2 - 1, 0)); pRec.n = m_frame.n; pRec.pdf = m_invSurfaceArea; pRec.measure = EArea; pRec.uv = sample; } 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 << "Rectangle[" << endl << " objectToWorld = " << indent(m_objectToWorld.toString()) << "," << endl; if (isMediumTransition()) oss << " interiorMedium = " << indent(m_interiorMedium.toString()) << "," << endl << " exteriorMedium = " << indent(m_exteriorMedium.toString()) << "," << endl; oss << " bsdf = " << indent(m_bsdf.toString()) << "," << endl << " 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: Transform m_objectToWorld; Transform m_worldToObject; Frame m_frame; Vector m_dpdu, m_dpdv; Float m_invSurfaceArea; }; MTS_IMPLEMENT_CLASS_S(Rectangle, false, Shape) MTS_EXPORT_PLUGIN(Rectangle, "Rectangle intersection primitive"); MTS_NAMESPACE_END