diff options
Diffstat (limited to 'hw2/src/triangle.cpp')
-rw-r--r-- | hw2/src/triangle.cpp | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/hw2/src/triangle.cpp b/hw2/src/triangle.cpp new file mode 100644 index 0000000..1e3990e --- /dev/null +++ b/hw2/src/triangle.cpp @@ -0,0 +1,451 @@ +/******************************************************************/ +/* 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 <cassert> +#include <algorithm> + +#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<std::vector<vec3d>>(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<const std::vector<vec3d>>& 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<std::vector<vec3d>>(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<const std::vector<vec3d>>& vertex_list, + size_t n1_idx, size_t n2_idx, size_t n3_idx, const std::shared_ptr<const std::vector<vec3d>>& 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<std::vector<vec2d>>(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<const std::vector<vec3d>>& vertex_list, + size_t t1_idx, size_t t2_idx, size_t t3_idx, const std::shared_ptr<const std::vector<vec2d>>& 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<std::vector<vec2d>>(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<const std::vector<vec3d>>& vertex_list, + size_t n1_idx, size_t n2_idx, size_t n3_idx, const std::shared_ptr<const std::vector<vec3d>>& normal_list, + size_t t1_idx, size_t t2_idx, size_t t3_idx, const std::shared_ptr<const std::vector<vec2d>>& 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); +} |