using System; using Sharp3D.Math.Core; namespace PathTracer { public class Triangle:Intersectable { Vector3F pointA, pointB, pointC; Vector3F normA, normB, normC; float xmin, ymin, zmin, xmax, ymax, zmax; Plane surfacePlane; Material surfaceMaterial; bool isLightSource; public Triangle (Vector3F pointA, Vector3F pointB, Vector3F pointC) { this.pointA = pointA; this.pointB = pointB; this.pointC = pointC; Vector3F normal = getNormal (); surfacePlane = new Plane (normal, pointA); computeBoundaries (); } public Triangle (Vector3F pointA, Vector3F pointB, Vector3F pointC, Material mat) { this.pointA = pointA; this.pointB = pointB; this.pointC = pointC; surfaceMaterial = mat; Vector3F normal = getNormal (); surfacePlane = new Plane (normal, pointA); } public Triangle (Vector3F a, Vector3F b, Vector3F c, Vector3F normA, Vector3F normB, Vector3F normC) { this.pointA = a; this.pointB = b; this.pointC = c; this.normA = normA; this.normB = normB; this.normC = normC; normA.Normalize (); normB.Normalize (); normC.Normalize (); Vector3F normal = getNormal (); surfacePlane = new Plane (normal, a); computeBoundaries (); } public Triangle (Vector3F a, Vector3F b, Vector3F c, Vector3F normA, Vector3F normB, Vector3F normC, Material mat) { this.pointA = a; this.pointB = b; this.pointC = c; this.normA = normA; this.normB = normB; this.normC = normC; normA.Normalize (); normB.Normalize (); normC.Normalize (); Vector3F normal = getNormal (); surfacePlane = new Plane (normal, a); computeBoundaries (); surfaceMaterial = mat; } private Vector3F getNormal () { Vector3F ab = Vector3F.Subtract (pointB, pointA); ab.Normalize (); Vector3F ac = Vector3F.Subtract (pointC, pointA); ac.Normalize (); Vector3F normal = Vector3F.CrossProduct (ab, ac); return normal; } public Vector3F getNormal(float beta, float gamma) { // if the vertex normals have not been set, return the triangle plane's // normal Vector3F ret; if (normA == null) { return surfacePlane.getNormal(); } else { // we return the linear interpolation based on the beta and // gamma factors of the normals. Vector3F Bfactor = new Vector3F(normB); Bfactor = Vector3F.Multiply(Bfactor,beta); Vector3F Cfactor = new Vector3F(normC); Cfactor = Vector3F.Multiply(Cfactor,gamma); Vector3F Afactor = new Vector3F(normA); Afactor = Vector3F.Multiply(Afactor,1 - beta - gamma); ret = new Vector3F(Afactor); ret = Vector3F.Add(ret,Bfactor); ret = Vector3F.Add(ret,Cfactor); ret.Normalize(); } return ret; } public void computeBoundaries () { xmin = pointA.X; ymin = pointA.Y; zmin = pointA.Z; xmax = pointA.X; ymax = pointA.Y; zmax = pointA.Z; if (pointB.X < xmin) { xmin = pointB.X; } else if (pointC.X < xmin) { xmin = pointC.X; } if (pointB.Y < ymin) { ymin = pointB.Y; } else if (pointC.Y < ymin) { ymin = pointC.Y; } if (pointB.Z < zmin) { zmin = pointB.Z; } else if (pointC.Z < zmin) { zmin = pointC.Z; } if (pointB.X > xmax) { xmax = pointB.X; } else if (pointC.X > xmax) { xmax = pointC.X; } if (pointB.Y > ymax) { ymax = pointB.Y; } else if (pointC.Y > ymax) { ymax = pointC.Y; } if (pointB.Z > zmax) { zmax = pointB.Z; } else if (pointC.Z > zmax) { zmax = pointC.Z; } } #region Intersectable implementation Hitrecord Intersectable.Intersect (Ray r) { Matrix3F A, Ax; float beta, gamma, t; Hitrecord hit; Vector3F hitPoint; Vector3F sol = new Vector3F (pointA.X - r.getOrigin ().X, pointA.Y - r.getOrigin ().Y, pointA.Z - r.getOrigin ().Z); A = new Matrix3F (); A.M11 = pointA.X - pointB.X; A.M12 = pointA.X - pointC.X; A.M13 = r.getDir().X; A.M21 = pointA.Y - pointB.Y; A.M22 = pointA.Y - pointC.Y; A.M23 = r.getDir().Z; A.M31 = pointA.Z - pointB.Z; A.M32 = pointA.Z - pointC.Z; A.M33 = r.getDir().Z; if (A.Determinant() < -Material.EPSILON || A.Determinant () > Material.EPSILON) { // solving using // Cramer's rule float Adet = A.Determinant (); Ax = new Matrix3F (A); Ax.M11 = sol.X; Ax.M21 = sol.Y; Ax.M31 = sol.Z; beta = Ax.Determinant () / Adet; Ax = new Matrix3F (A); Ax.M21 = sol.X; Ax.M22=sol.Y; Ax.M23 = sol.Z; gamma = Ax.Determinant () / Adet; Ax = new Matrix3F (A); Ax.M31 = sol.X; Ax.M32 = sol.Y; Ax.M33 = sol.Z; t = Ax.Determinant () / Adet; // if beta + gamma are // between 0 and 1, we're in // the triangle. if (beta + gamma < 1 + Material.EPSILON && beta > -Material.EPSILON && gamma > -Material.EPSILON) { hitPoint = new Vector3F (r.getDir ()); hitPoint = Vector3F.Multiply (hitPoint, t); hitPoint = Vector3F.Add (hitPoint, r.getOrigin ()); hit = new Hitrecord (hitPoint, t, getNormal (beta, gamma), ((Intersectable)this).getMaterial (), this); return hit; } } return null; } public Vector3F getA () { return pointA; } public Vector3F getB () { return pointB; } public Vector3F getC () { return pointC; } public Vector3F getNormA () { return normA; } public Vector3F getNormB () { return normB; } public Vector3F getNormC () { return normC; } public float getXmax () { return xmax; } public float getXmin () { return xmin; } public float getYmax () { return ymax; } public float getYmin () { return ymin; } public float getZmax () { return zmax; } public float getZmin () { return zmin; } public bool isAggregate () { return false; } Material Intersectable.getMaterial () { return surfaceMaterial; } bool Intersectable.isLightSource () { return isLightSource; } #endregion } }