/* Triangle.cpp * * Definition of the Triangle class. */ #include "Triangle.h" #include #include using namespace std; const Tuple Triangle::DEF_COLOR = Tuple(1,1,1); // set default color to white const float Triangle::SHININESS = 10.0; const double Triangle::MAX_SHRINKFACTOR = 1000.0; const double Triangle::MIN_SHRINKFACTOR = 0.0; const double Triangle::DEF_SHRINKFACTOR = 1000.0; double Triangle::shrinkFactor_ = DEF_SHRINKFACTOR; bool Triangle::crazyTris_ = false; Triangle::Triangle(Vertex* v1, Vertex* v2, Vertex* v3) : Obstruction(), v1_(v1), v2_(v2), v3_(v3), colors_(), isHole_(false) { // Make sure pointers are non-null assert(v1_ && v2_ && v3_); #ifdef vertConv Vertex* tmp = v2; v2 = v3; v3 = tmp; #endif // Relative vectors for each of the vertices (i.e., distances from v1) Tuple rv1 = Tuple(); Tuple rv2 = Tuple(v2->v() - v1->v()); Tuple rv3 = Tuple(v3->v() - v1->v()); // Unit vectors about the coordinate system defined by the triangle // xNormal : direction along v2 // yNormal : the normal of the triangle's face // zNormal : the other direction // Note, these calculations check that the triangle is not colinear Tuple xNormal = (rv2).norm(); Tuple yNormal = (rv3).cross(rv2).norm(); Tuple zNormal = xNormal.cross(yNormal); // Find the standardized vertices sv1_ = Tuple(); sv2_ = Tuple( (rv2).length(), 0.0, 0.0); sv3_ = Tuple( (xNormal * xNormal.dot(rv3)).length(), 0.0, (zNormal * zNormal.dot(rv3)).length()); // Record the face normal for later use normal_ = yNormal; /* We must now calculate the rotation matrix used to transform paths when * testing for intersection. We will do this by composing three rotations * about the axes (or two in the case that rv2 is straight up). After * computing each rotation, we'll transform the triangle's vertices to make * it easier to compute the next rotation. */ // Check if rv2 is straight up; if so, we shouldn't rotate on the y-axis Tuplex yRotation; Tuple yv1; Tuple yv2; Tuple yv3; if (rv2.x() != 0.0 || rv2.z() != 0.0) { // Compute a and b, where a + bi is a unit complex factor of rotation // for rotating rv2 such that it's in the xy-plain double a1 = Tuple(1.0, 0.0, 0.0).dot(Tuple(rv2.x(), 0.0, rv2.z()).norm()); double b1 = Tuple(0.0, 0.0, 1.0).dot(Tuple(rv2.x(), 0.0, rv2.z()).norm()); // Define a matrix to compute complex multiplcation (a + bi) * (z + xi) yRotation = Tuplex( Tuple(a1, 0.0, b1), Tuple(0.0, 1.0, 0.0), Tuple(-b1, 0.0, a1)); // Rotate the triangle about the y-axis yv1 = yRotation * rv1; yv2 = yRotation * rv2; yv3 = yRotation * rv3; } else { // No rotation necessary yRotation = Tuplex( Tuple(1.0, 0.0, 0.0), Tuple(0.0, 1.0, 0.0), Tuple(0.0, 0.0, 1.0)); yv1 = rv1; yv2 = rv2; yv3 = rv3; } // Compute a and b, where a + bi is a unit complex constant of rotation // for rotating rv2 such that it's along the x-axis // Note, this rotaion should be negative, so negate b double a2 = Tuple(1.0, 0.0, 0.0).dot(Tuple(yv2.x(), yv2.y(), 0.0).norm()); double b2 = -Tuple(0.0, 1.0, 0.0).dot(Tuple(yv2.x(), yv2.y(), 0.0).norm()); // Define a matrix to compute complex multiplcation (a + bi) * (x + yi) Tuplex zRotation( Tuple(a2, -b2, 0.0), Tuple(b2, a2, 0.0), Tuple(0.0, 0.0, 1.0)); // Rotate the triangle about the z-axis Tuple zv1 = zRotation * yv1; Tuple zv2 = zRotation * yv2; Tuple zv3 = zRotation * yv3; // Compute a and b, where a + bi is a unit complex constant of rotation // for rotating rv3 such that it's in the xz-plain // Note, this rotaion should be negative, so negate b double a3 = Tuple(0.0, 0.0, 1.0).dot(Tuple(0.0, zv3.y(), zv3.z()).norm()); double b3 = -Tuple(0.0, 1.0, 0.0).dot(Tuple(0.0, zv3.y(), zv3.z()).norm()); // Define a matrix to compute complex multiplcation (a + bi) * (z + yi) Tuplex xRotation( Tuple(1.0, 0.0, 0.0), Tuple(0.0, a3, b3), Tuple(0.0, -b3, a3)); // Compose the rotations rotation_ = xRotation * zRotation * yRotation; // Set up colors colors_.resize(NUM_OF_COLORS); for (int i = 0; i < (int)colors_.size(); ++i) { colors_[i] = DEF_COLOR; } } Triangle::~Triangle() { // Nothing to do } void Triangle::setColors(const vector& colors) { assert(colors.size() == NUM_OF_COLORS); colors_ = colors; } /* draw * * Draw the triangle at the given coordinates. */ void Triangle::draw(bool light) const { // Get the tuples from the vertices for easier access Tuple t1 = v1_->v(); Tuple t2 = v2_->v(); Tuple t3 = v3_->v(); Tuple t1n = v1_->litNormal(); Tuple t2n = v2_->litNormal(); Tuple t3n = v3_->litNormal(); if (light) { glPushMatrix(); if (crazyTris_) { Tuple pos = avgPos(); glTranslatef(pos.x(), pos.y(), pos.z()); } GLfloat matShininess[] = {SHININESS}; Tuple color = ambient(); GLfloat ambNDiff[] = {color.x(), color.y(), color.z(), 1.0}; glMaterialfv(GL_FRONT, GL_SHININESS, matShininess); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, ambNDiff); glBegin(GL_TRIANGLES); glNormal3f(t1n.x(),t1n.y(),t1n.z()); glVertex3f(t1.x(), t1.y(), t1.z()); glNormal3f(t2n.x(),t2n.y(),t2n.z()); glVertex3f(t2.x(), t2.y(), t2.z()); glNormal3f(t3n.x(),t3n.y(),t3n.z()); glVertex3f(t3.x(), t3.y(), t3.z()); glEnd(); glPopMatrix(); /* GLfloat v1Color[] = {v1_->r(), v1_->g(), v1_->b(), 1.0}; GLfloat v2Color[] = {v2_->r(), v2_->g(), v2_->b(), 1.0}; GLfloat v3Color[] = {v3_->r(), v3_->g(), v3_->b(), 1.0}; glBegin(GL_TRIANGLES); glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,v1Color); glNormal3f(t1n.x(),t1n.y(),t1n.z()); glVertex3f(t1.x(), t1.y(), t1.z()); glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,v2Color); glNormal3f(t2n.x(),t2n.y(),t2n.z()); glVertex3f(t2.x(), t2.y(), t2.z()); glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,v3Color); glNormal3f(t3n.x(),t3n.y(),t3n.z()); glVertex3f(t3.x(), t3.y(), t3.z()); glEnd(); */ } else { // Draw a triangle with position an colors based on the // triangle's three constituent vertices glPushMatrix(); glBegin(GL_TRIANGLES); glNormal3f(t1n.x(),t1n.y(),t1n.z()); glColor3f(v1_->r(), v1_->g(), v1_->b()); glVertex3f(t1.x(), t1.y(), t1.z()); glNormal3f(t2n.x(),t2n.y(),t2n.z()); glColor3f(v2_->r(), v2_->g(), v2_->b()); glVertex3f(t2.x(), t2.y(), t2.z()); glNormal3f(t3n.x(),t3n.y(),t3n.z()); glColor3f(v3_->r(), v3_->g(), v3_->b()); glVertex3f(t3.x(), t3.y(), t3.z()); glEnd(); glPopMatrix(); } } /* pathIntersect * * Determines if this triangle is on the given path and returns the Beta value, * (the percent of path completed before collision). */ const double Triangle::pathIntersect( const Path& path, double radius) const { // Standardize input path to match standardized triangle vertices Path sPath = path.translate(sv1_ - v1_->v()).rotate(rotation_); // Define a few helpful variables to clarify the following code double y1 = sPath.endPos().y(); double y0 = sPath.begPos().y(); // Check if the path moves downwards through the buffer on the xz-plain; // offset by the ball's radius so that we consider the edge of the ball // rather than it's center if (y1 < y0 && y0 >= 0.0 + radius && y1 < 0.0 + radius + BUFFER) { // We'll fill this with the distance along the path where the ball // touches the buffer on the xz-plane double beta; // If the ball starts in the buffer, collide instantly; otherwise // just collide when the ball reaches the buffer if (y0 < 0.0 + radius + BUFFER) beta = 0.0; else beta = (radius + BUFFER - y0) / (y1 - y0); // Make sure beta is in range if (beta >= 0.0 && beta < 1.0) { // Find where the ball touches the xz-plain Tuple touchPos = sPath.begPos() + beta * (sPath.endPos() - sPath.begPos()); // Define a few helpful variables to clarify the following code double xt = touchPos.x(); double zt = touchPos.z(); // Check if the path moves through the triangle if (zt > 0.0 && xt > zt * (sv3_.x() / sv3_.z()) && xt < zt * ((sv3_.x() - sv2_.x()) / sv3_.z()) + sv2_.x()) { return beta; } } } // Add EPSILON to err on the safe side (no collision) return 1.0 + EPSILON; } /* calcNormal * * Returns the normal of the triangle. */ const Tuple Triangle::calcNormal(const Tuple& point) const { (void)point; return normal_; } const Tuple Triangle::avgPos() const { Tuple avg = v1_->v() + v2_->v() + v3_->v(); return avg /= shrinkFactor_; } void Triangle::toggleCrazyTris() { crazyTris_ = !crazyTris_; if (!crazyTris_) { shrinkFactor_ = DEF_SHRINKFACTOR; cout << "Crazy Triangles: Deactivated" << endl; } else { cout << "Crazy Triangles: Activated" << endl; } } void Triangle::setCrazyTris() { crazyTris_ = true; cout << "Crazy Triangles: Activated" << endl; } void Triangle::unsetCrazyTris() { shrinkFactor_ = DEF_SHRINKFACTOR; crazyTris_ = false; cout << "Crazy Triangles: Deactivated" << endl; } void Triangle::setShrinkFactor(double shrinkFactor) { shrinkFactor_ = shrinkFactor; if (shrinkFactor_ > MAX_SHRINKFACTOR) shrinkFactor_ = MAX_SHRINKFACTOR; if (shrinkFactor_ < MIN_SHRINKFACTOR) shrinkFactor_ = MIN_SHRINKFACTOR; } void Triangle::shrink() { if (!crazyTris_) return; shrinkFactor_ *= 1.2; if (shrinkFactor_ > MAX_SHRINKFACTOR) shrinkFactor_ = MAX_SHRINKFACTOR; cout << "Conjoining Triangles\n"; } void Triangle::enlarge() { if (!crazyTris_) return; shrinkFactor_ /= 1.2; if (shrinkFactor_ < MIN_SHRINKFACTOR) shrinkFactor_ = MIN_SHRINKFACTOR; cout << "Separating Triangles\n"; }