/******************************************************************/ /* This file is part of the homework assignments for CSCI-427/527 */ /* at The College of William & Mary and authored by Pieter Peers. */ /* No part of this file, whether altered or in original form, can */ /* be distributed or used outside the context of CSCI-427/527 */ /* without consent of either the College of William & Mary or */ /* Pieter Peers. */ /******************************************************************/ #include #include #include "triangle.h" #include "constants.h" ////////////////// // Constructors // ////////////////// triangle::triangle(void) { _vertex_list = nullptr; _normal_list = nullptr; _textureCoord_list = nullptr; } ////////////////////// // Copy Constructor // ////////////////////// triangle::triangle(const triangle& t) : triangle() { // share ptr to list _vertex_list = t._vertex_list; _normal_list = t._normal_list; _textureCoord_list = t._textureCoord_list; // copy indices _vertex_idx = t._vertex_idx; _normal_idx = t._normal_idx; _textureCoord_idx = t._textureCoord_idx; } ////////////////////// // Move Constructor // ////////////////////// triangle::triangle(triangle&& t) : triangle() { // swap list (no need to increment ref-count) std::swap(_vertex_list, t._vertex_list); std::swap(_normal_list, t._normal_list); std::swap(_textureCoord_list, t._textureCoord_list); // copy indices _vertex_idx = t._vertex_idx; _normal_idx = t._normal_idx; _textureCoord_idx = t._textureCoord_idx; } ////////////////////////// // General Constructors // ////////////////////////// triangle::triangle(const vec3d& v1, const vec3d& v2, const vec3d& v3) : triangle() { // create a list of three vertices auto vertex_list = std::make_shared>(3); _vertex_list = vertex_list; // copy values (*vertex_list)[0] = v1; (*vertex_list)[1] = v2; (*vertex_list)[2] = v3; // set indices _vertex_idx = {0,1,2}; } triangle::triangle(size_t v1_idx, size_t v2_idx, size_t v3_idx, const std::shared_ptr>& vertex_list) : triangle() { // share vertex list _vertex_list = vertex_list; // copy indices _vertex_idx[0] = v1_idx; _vertex_idx[1] = v2_idx; _vertex_idx[2] = v3_idx; } triangle::triangle(const vec3d& v1, const vec3d& v2, const vec3d& v3, const vec3d& n1, const vec3d& n2, const vec3d& n3) : triangle(v1, v2, v3) { // create a list of three normals auto normal_list = std::make_shared>(3); _normal_list = normal_list; // copy values (*normal_list)[0] = n1; (*normal_list)[1] = n2; (*normal_list)[2] = n3; // set indices _normal_idx = {0,1,2}; } triangle::triangle(size_t v1_idx, size_t v2_idx, size_t v3_idx, const std::shared_ptr>& vertex_list, size_t n1_idx, size_t n2_idx, size_t n3_idx, const std::shared_ptr>& normal_list) : triangle(v1_idx, v2_idx, v3_idx, vertex_list) { // share normal list _normal_list = normal_list; // copy indices _normal_idx[0] = n1_idx; _normal_idx[1] = n2_idx; _normal_idx[2] = n3_idx; } triangle::triangle(const vec3d& v1, const vec3d& v2, const vec3d& v3, const vec2d& t1, const vec2d& t2, const vec2d& t3) : triangle(v1, v2, v3) { // create a list of three texture coordinates auto textureCoord_list = std::make_shared>(3); _textureCoord_list = textureCoord_list; // copy values (*textureCoord_list)[0] = t1; (*textureCoord_list)[1] = t2; (*textureCoord_list)[2] = t3; // set indices _textureCoord_idx = {0,1,2}; } triangle::triangle(size_t v1_idx, size_t v2_idx, size_t v3_idx, const std::shared_ptr>& vertex_list, size_t t1_idx, size_t t2_idx, size_t t3_idx, const std::shared_ptr>& texcoord_list) : triangle(v1_idx, v2_idx, v3_idx, vertex_list) { // share normal list _textureCoord_list = texcoord_list; // copy indices _textureCoord_idx[0] = t1_idx; _textureCoord_idx[1] = t2_idx; _textureCoord_idx[2] = t3_idx; } triangle::triangle(const vec3d& v1, const vec3d& v2, const vec3d& v3, const vec3d& n1, const vec3d& n2, const vec3d& n3, const vec2d& t1, const vec2d& t2, const vec2d& t3) : triangle(v1, v2, v3, n1, n2, n3) { // create a list of three texture coordinates auto textureCoord_list = std::make_shared>(3); _textureCoord_list = textureCoord_list; // copy values (*textureCoord_list)[0] = t1; (*textureCoord_list)[1] = t2; (*textureCoord_list)[2] = t3; // set indices _textureCoord_idx = {0,1,2}; } triangle::triangle(size_t v1_idx, size_t v2_idx, size_t v3_idx, const std::shared_ptr>& vertex_list, size_t n1_idx, size_t n2_idx, size_t n3_idx, const std::shared_ptr>& normal_list, size_t t1_idx, size_t t2_idx, size_t t3_idx, const std::shared_ptr>& texcoord_list) : triangle(v1_idx, v2_idx, v3_idx, vertex_list, n1_idx, n2_idx, n3_idx, normal_list) { // share normal list _textureCoord_list = texcoord_list; // copy indices _textureCoord_idx[0] = t1_idx; _textureCoord_idx[1] = t2_idx; _textureCoord_idx[2] = t3_idx; } //////////////// // Inspectors // //////////////// const vec3d& triangle::vertex(size_t index) const { // bounds check assert(_vertex_list && index < 3); // Done. return (*_vertex_list)[_vertex_idx[index]]; } const vec3d& triangle::normal(size_t index) const { // bounds check assert(_normal_list && index < 3); // Done. return (*_normal_list)[_normal_idx[index]]; } const vec2d& triangle::textureCoordinate(size_t index) const { // bounds check assert(_textureCoord_list && index < 3); // Done. return (*_textureCoord_list)[_textureCoord_idx[index]]; } bool triangle::hasPerVertexNormals(void) const { return (bool)(_normal_list); } bool triangle::hasPerVertexTextureCoordinates(void) const { return (bool)(_textureCoord_list); } /////////////// // Operators // /////////////// triangle& triangle::operator=(const triangle& t) { _assign(t); return *this; } triangle& triangle::operator=(triangle&& t) { // swap list (no need to increment ref-count) std::swap(_vertex_list, t._vertex_list); std::swap(_normal_list, t._normal_list); std::swap(_textureCoord_list, t._textureCoord_list); // copy indices _vertex_idx = t._vertex_idx; _normal_idx = t._normal_idx; _textureCoord_idx = t._textureCoord_idx; // Done. return *this; } ///////////// // Methods // ///////////// bool triangle::intersect(const ray& r, vec3d& barycentricCoord, float& t) const { // HW2: Implement this. // Compute the intersection between the triangle (this) and the ray (r). // Use the method 'vertex(i)' to access the i-th vertex of the triangle (i=[0,1,2]). // Use the method 'normal()' to access the normal of the plane in which the traingle lies. // Returns: bool (true if hit, false if not) // barycentricCoord (fill in alpha, beta, gamma barycentric coordinates // of (vertex(0), vertex(1), and vertex(2)) if hit) // t (the distance at which the ray intersects => r(t) == intersection point) float d, a1, a2, a3, area, alpha, beta, gamma; vec3d n, q, p1, p2, p3; d = -(vertex(0).dot(normal())); t = (-((r.origin().dot(normal())) + d)) / (r.direction().dot(normal())); if (t < 0) {return false;} q = r.origin() + t * r.direction(); p1 = vertex(0); p2 = vertex(1); p3 = vertex(2); n = (p2 - p1).cross(p3 - p1); a1 = 0.5f * (p2 - q).cross(p3 - q).dot(n); a2 = 0.5f * (p3 - q).cross(p1 - q).dot(n); a3 = 0.5f * (p1 - q).cross(p2 - q).dot(n); area = a1 + a2 + a3; alpha = a1 / area; beta = a2 / area; gamma = a3 / area; if (alpha < 0 || beta < 0 || gamma < 0) {return false;} barycentricCoord = vec3d(alpha, beta, gamma); return true; } boundingBox triangle::boundingbox(void) const { boundingBox bb; for(unsigned int i=0; i < 3; i++) bb += vertex(i); return bb; } vec3d triangle::vertex(const vec3d& barycentricCoord) const { vec3d result(0.0f); for(unsigned int i=0; i < 3; i++) result += vertex(i) * barycentricCoord[i]; return result; } vec3d triangle::normal(void) const { vec3d e1 = vertex(1) - vertex(0); vec3d e2 = vertex(2) - vertex(0); return e1.cross(e2).normalize(); } vec3d triangle::shadingAxis(void) const { // follow texture coordinates if defined, otherwise, return the first axis. if(!_textureCoord_list) return normalize(vertex(1)-vertex(0)); // find the vector that follows the U-axis of the texture coordinates // solve: // [ (t1-t0) (t2-t0) ] [ delta_beta delta_gamma ]^T = [1 0]^T // for delta_beta and delta_gamma. vec3d v1 = vertex(1) - vertex(0); vec3d v2 = vertex(2) - vertex(0); vec2d t1 = textureCoordinate(1) - textureCoordinate(0); vec2d t2 = textureCoordinate(2) - textureCoordinate(0); // check special cases where U or V is aligned with a vertex if( fabs(t1.v) < EPSILON ) return normalize(t1.u*v1); if( fabs(t2.v) < EPSILON ) return normalize(t2.u*v2); // compute delta_beta float inv_delta_beta = t1.u - t1.v*t2.u/t2.v; // => degenrate case if(fabs(inv_delta_beta) < EPSILON) return normalize(v1); float delta_beta = 1.0f / inv_delta_beta; float delta_gamma = -delta_beta * t1.v / t2.v; // compute U return normalize( delta_beta*v1 + delta_gamma*v2 ); } vec3d triangle::normal(const vec3d& barycentricCoord) const { // sanity check if(!hasPerVertexNormals()) return normal(); // interpolate vec3d result(0.0f); for(unsigned int i=0; i < 3; i++) result += normal(i) * barycentricCoord[i]; return result.normalize(); } vec2d triangle::textureCoordinate(const vec3d& barycentricCoord) const { // sanity check if(!hasPerVertexTextureCoordinates()) return vec2d(); // interpolate vec2d result(0.0f); for(unsigned int i=0; i < 3; i++) result += textureCoordinate(i) * barycentricCoord[i]; return result; } float triangle::area(void) const { vec3d e1 = vertex(1) - vertex(0); vec3d e2 = vertex(2) - vertex(0); return 0.5f * sqrt( e1.squared_length() * e2.squared_length() ); } vec3d triangle::sample(float r1, float r2, vec3d& barycentricCoord, float& pdf) const { r2 = sqrt(r2); // compute barycentric coordinate barycentricCoord.x = r1*r2; barycentricCoord.y = (1.0f - r2); barycentricCoord.z = 1.0f - barycentricCoord.x - barycentricCoord.y; // compute pdf pdf = 1.0f / area(); // Done. return vertex(barycentricCoord); } ///////////////////// // Private Methods // ///////////////////// void triangle::_assign(const triangle& t) { // avoid self-assign if(&t == this) return; // share ptr to list _vertex_list = t._vertex_list; _normal_list = t._normal_list; _textureCoord_list = t._textureCoord_list; // copy indices _vertex_idx = t._vertex_idx; _normal_idx = t._normal_idx; _textureCoord_idx = t._textureCoord_idx; } void triangle::_swap(triangle& t) { // copy list ptr std::swap(_vertex_list, t._vertex_list); std::swap(_normal_list, t._normal_list); std::swap(_textureCoord_list, t._textureCoord_list); // copy indices std::swap(_vertex_idx, t._vertex_idx); std::swap(_normal_idx, t._normal_idx); std::swap(_textureCoord_idx, t._textureCoord_idx); }