/******************************************************************/ /* 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 { // Ray-Triangle intersection [Moller and Trumbore 1997] // // [t ] 1 [ ((r.origin-v0) x (v1-v0)).(v2-v0) ] // [barycenter.y] = -------------------------- [ (r.dir x (v2-v0)).(r.origin-v0) ] // [barycenter.z] ((r.dir x (v2-vo)).(v1-v0) [ ((r.origin-v0) x (v1-v0)).r.dir ] // // compute denominator vec3d e1 = vertex(1) - vertex(0); vec3d e2 = vertex(2) - vertex(0); vec3d p = r.direction().cross(e2); float denom = p.dot(e1); // check if parallel (denominator == 0) if(fabs(denom) < EPSILON) return false; denom = 1.0f / denom; // compute barycentricCoord.y vec3d diff = r.origin() - vertex(0); barycentricCoord.y = denom * p.dot(diff); // check if barycenter.y inside triangle if(barycentricCoord.y < -EPSILON || barycentricCoord.y > 1.0f+EPSILON) return false; // compute barycentricCoord.z vec3d diffXe1 = diff.cross(e1); barycentricCoord.z = denom * diffXe1.dot(r.direction()); // check if barycenter.z inside triangle if(barycentricCoord.z < -EPSILON || barycentricCoord.z > 1.0f+EPSILON) return false; // compute barycenter.x and check if inside barycentricCoord.x = 1.0f - barycentricCoord.y - barycentricCoord.z; if(barycentricCoord.y + barycentricCoord.z > 1.0f+EPSILON) return false; // compute t t = denom * diffXe1.dot(e2); // check if in front (i.e., t>0) if(t < EPSILON) return false; // Clamp (to compensate for roundoff errors) barycentricCoord.clamp(0.0f, 1.0f); // Done. 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 * e1.cross(e2).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); }