public virtual double Pdf(Point p, Vector wi)
        {
            // Intersect sample ray with area light geometry
            DifferentialGeometry dgLight;
            Ray ray = new Ray(p, wi, 1e-3d);
            ray.depth = -1; // temporary hack to ignore alpha mask
            double thit, rayEpsilon;
            if (!Intersect(ray, out thit, out rayEpsilon, out dgLight)) return 0.0d;

            // Convert light sample weight to solid angle measure
            double pdf =  Geometry.DistanceSquared(p, ray.GetPointAt(thit)) / ( Geometry.AbsDot(dgLight.nn, -wi) * Area());
            if (Double.IsInfinity(pdf)) pdf = 0.0d;
            return pdf;
        }
        public override bool IntersectP(Ray r)
        {
            // Compute $\VEC{s}_1$

            // Get triangle vertices in _p1_, _p2_, and _p3_
            Point p1 = mesh.p[v[0]];
            Point p2 = mesh.p[v[1]];
            Point p3 = mesh.p[v[2]];
            Vector e1 = p2 - p1;
            Vector e2 = p3 - p1;
            Vector s1 = Geometry.Cross(r.d, e2);
            double divisor = Geometry.Dot(s1, e1);

            if (divisor == 0.0d)
                return false;
            double invDivisor = 1.0d / divisor;

            // Compute first barycentric coordinate
            Vector d = r.o - p1;
            double b1 = Geometry.Dot(d, s1) * invDivisor;
            if (b1 < 0.0d || b1 > 1.0d)
                return false;

            // Compute second barycentric coordinate
            Vector s2 = Geometry.Cross(d, e1);
            double b2 = Geometry.Dot(r.d, s2) * invDivisor;
            if (b2 < 0.0d || b1 + b2 > 1.0d)
                return false;

            // Compute _t_ to intersection point
            double t = Geometry.Dot(e2, s2) * invDivisor;
            if (t < r.mint || t > r.maxt)
                return false;

            // Test shadow r intersection against alpha texture, if present
            if (r.depth != -1 && mesh.alphaTexture != null)
            {
                // Compute triangle partial derivatives
                Vector dpdu, dpdv;
                double[,] uvs = new double[3, 2];
                GetUVs(uvs);

                // Compute deltas for triangle partial derivatives
                double du1 = uvs[0, 0] - uvs[2, 0];
                double du2 = uvs[1, 0] - uvs[2, 0];
                double dv1 = uvs[0, 1] - uvs[2, 1];
                double dv2 = uvs[1, 1] - uvs[2, 1];
                Vector dp1 = p1 - p3, dp2 = p2 - p3;
                double determinant = du1 * dv2 - dv1 * du2;
                if (determinant == 0.0d)
                {
                    // Handle zero determinant for triangle partial derivative matrix
                    Geometry.CoordinateSystem(Geometry.Normalize(Geometry.Cross(e2, e1)), out dpdu, out dpdv);
                }
                else
                {
                    double invdet = 1.0d / determinant;
                    dpdu = (dv2 * dp1 - dv1 * dp2) * invdet;
                    dpdv = (-du2 * dp1 + du1 * dp2) * invdet;
                }

                // Interpolate $(u,v)$ triangle parametric coordinates
                double b0 = 1 - b1 - b2;
                double tu = b0 * uvs[0, 0] + b1 * uvs[1, 0] + b2 * uvs[2, 0];
                double tv = b0 * uvs[0, 1] + b1 * uvs[1, 1] + b2 * uvs[2, 1];
                DifferentialGeometry dgLocal = new DifferentialGeometry(r.GetPointAt(t), dpdu, dpdv, new Normal(0, 0, 0), new Normal(0, 0, 0), tu, tv, this);
                if (mesh.alphaTexture.Evaluate(dgLocal) == 0.0d)
                    return false;
            }

            return true;
        }
        public override Point Sample(Point p, double u1, double u2, ref Normal ns)
        {
            // Compute coordinate system for sphere sampling
            Point Pcenter = ObjectToWorld.Apply(new Point(0, 0, 0));
            Vector wc = Geometry.Normalize(Pcenter - p);
            Vector wcX, wcY;
            Geometry.CoordinateSystem(wc, out wcX, out wcY);

            // Sample uniformly on sphere if $\pt{}$ is inside it
            if (Geometry.DistanceSquared(p, Pcenter) - radius * radius < 1e-4f)
                return Sample(u1, u2, ref ns);

            // Sample sphere uniformly inside subtended cone
            double sinThetaMax2 = radius * radius / Geometry.DistanceSquared(p, Pcenter);
            double cosThetaMax = Math.Sqrt(Math.Max(0.0d, 1.0d - sinThetaMax2));
            DifferentialGeometry dgSphere;
            double thit, rayEpsilon;
            Point ps;
            Ray r = new Ray(p, MonteCarlo.UniformSampleCone(u1, u2, cosThetaMax, wcX, wcY, wc), 1e-3d);
            if (!Intersect(r, out thit, out rayEpsilon, out dgSphere))
                thit = Geometry.Dot(Pcenter - p, Geometry.Normalize(r.d));
            ps = r.GetPointAt(thit);
            ns = new Normal(Geometry.Normalize(ps - Pcenter));
            if (ReverseOrientation) ns *= -1.0d;
            return ps;
        }