package jpbrt.shapes;
import jpbrt.core.BBox;
import jpbrt.core.DifferentialGeometry;
import jpbrt.core.Global;
import jpbrt.core.Intersection;
import jpbrt.core.Normal;
import jpbrt.core.Point;
import jpbrt.core.Ray;
import jpbrt.core.Shape;
import jpbrt.core.Transform;
import jpbrt.core.Vector;
public class Triangle extends Shape
{
private TriangleMesh mesh;
private int v1, v2, v3;
public Triangle(Transform o2w, Transform w2o, boolean ro,
TriangleMesh mesh, int idx)
{
super(o2w, w2o, ro);
this.mesh = mesh;
this.v1 = mesh.vertexIndices[idx];
this.v2 = mesh.vertexIndices[idx+1];
this.v3 = mesh.vertexIndices[idx+2];
}
@Override
public BBox objectBound()
{
Point p1 = mesh.p[v1];
Point p2 = mesh.p[v2];
Point p3 = mesh.p[v3];
BBox bbox = new BBox( worldToObject.transform(p1), worldToObject.transform(p2) );
return bbox.unionLocal( worldToObject.transform(p3) );
}
@Override
public BBox worldBound()
{
Point p1 = mesh.p[v1];
Point p2 = mesh.p[v2];
Point p3 = mesh.p[v3];
BBox bbox = new BBox(p1, p2);
return bbox.unionLocal(p3);
}
@Override
public Intersection intersect(Ray ray)
{
Intersection result = new Intersection();
// compute s1 = d x e2
Point p1 = mesh.p[v1];
Point p2 = mesh.p[v2];
Point p3 = mesh.p[v3];
Vector e1 = p1.to(p2);
Vector e2 = p1.to(p3);
Vector s1 = ray.d.cross(e2);
double divisor = s1.dot(e1);
if (divisor == 0)
return result;
double invDivisor = 1.0 / divisor;
// compute first barycentric coordinate b1
Vector d = p1.to(ray.o);
double b1 = d.dot(s1) * invDivisor;
if (b1 < 0 || b1 > 1)
return result;
// compute second barycentric coordinate b2
Vector s2 = d.cross(e1);
double b2 = ray.d.dot(s2) * invDivisor;
if (b2 < 0 || b2 > 1)
return result;
// compute t to intersection point
double t = e2.dot(s2) * invDivisor;
if (t < ray.mint || t > ray.maxt)
return result;
// compute triangle partial derivatives
Vector dpdu, dpdv;
double[][] uvs = getUVs();
// 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 = p3.to(p1);
Vector dp2 = p3.to(p2);
double determinant = du1 * dv2 - dv1 * du2;
if (determinant == 0)
{
dpdu = new Vector();
dpdv = new Vector();
Global.coordinateSystem(e2.cross(e1).normalizeLocal(), dpdu, dpdv);
}
else
{
double invdet = 1.0 / determinant;
dpdu = dp1.mul(dv2).sub(dp2.mul(dv1)).mulLocal(invdet);
dpdv = dp2.mul(du1).sub(dp1.mul(du2)).mulLocal(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];
// test intersection against alpha texture, if present
if (mesh.alphaTex != null)
{
Normal zeroN = new Normal(0, 0, 0);
DifferentialGeometry dgLocal = new DifferentialGeometry(ray.eval(t), dpdu, dpdv, zeroN, zeroN, tu, tv, this);
if (mesh.alphaTex.evaluate(dgLocal) == 0)
return result;
}
// fill in DifferetialGeometry from triangle hit
Normal zeroN = new Normal(0, 0, 0);
DifferentialGeometry dg = new DifferentialGeometry(ray.eval(t), dpdu, dpdv, zeroN, zeroN, tu, tv, this);
result.dg = dg;
result.tHit = t;
result.rayEpsilon = 1e-3 * t;
result.hit = true;
return result;
}
@Override
public boolean intersectP(Ray ray)
{
// TODO can be faster
return intersect(ray).hit;
}
@Override
public double area()
{
Point p1 = mesh.p[v1];
Point p2 = mesh.p[v2];
Point p3 = mesh.p[v3];
Vector v1 = p1.to(p2);
Vector v2 = p1.to(p3);
return 0.5 * v1.cross(v2).length();
}
@Override
public DifferentialGeometry getShadingGeometry(Transform obj2World, DifferentialGeometry dg)
{
if (mesh.n == null && mesh.s == null)
return dg;
// TODO
// initialize Triangle shading geometry with n and s
// compute barycentric coordinates for point
// use n and s to compute shading tangents for triangle, ss and ts
return dg;
}
public double[][] getUVs()
{
double[][] uv = new double[3][2];
if (mesh.uvs != null)
{
uv[0][0] = mesh.uvs[2 * v1];
uv[0][1] = mesh.uvs[2 * v1 + 1];
uv[1][0] = mesh.uvs[2 * v2];
uv[1][1] = mesh.uvs[2 * v2 + 1];
uv[2][0] = mesh.uvs[2 * v3];
uv[2][1] = mesh.uvs[2 * v3 + 1];
}
else
{
uv[0][0] = 0; uv[0][1] = 0;
uv[1][0] = 1; uv[1][1] = 0;
uv[2][0] = 1; uv[2][1] = 1;
}
return uv;
}
}