> > |
%META:TOPICINFO{author="AndrewKim" date="1078822743" format="1.0" version="1.1"}%
%META:TOPICPARENT{name="MiniGolf"}%
#include "cBall.h"
#include
#include
#include
using namespace std;
cBall::cBall()
{
position_ = cVector(0, 15, 7.00001); // a face collision
velocity_ = cVector(0, -2, 0);
radius_ = 1;
}
cBall::~cBall() { /* Nothing to do */ }
void cBall::update(float dt, cStaticTriangleMesh& staticObjects)
{
if(position_.y_ < -5) // just for the demo
{
float a = ((float)(rand()%100))/100.0f;
float b = ((float)(rand()%100))/100.0f;
float c = ((float)(rand()%100))/100.0f;
float d = ((float)(rand()%100))/100.0f;
float e = ((float)(rand()%100))/100.0f;
float f = ((float)(rand()%100))/100.0f;
position_ = cVector((a-.5)*20, 8, (c-.5)*20.0);
//position_ = cVector((a-.5), 15, (a-.5));
velocity_ = cVector((d-.5)/10, -2, (f-.5)/10);
radius_ = .5;//b/2 + .1;
}
/*
if(position_.y_ < -5)
{
velocity_ = cVector(0, 0, 0);
//position_ = cVector(0, 15, 7.0000); // parallel to face drop (height = +5)
//position_ = cVector(0, 15, 9.00001); // parallel to face drop (height = +5)
//position_ = cVector(1.5, 15, 2); // single face collision (height = +5)
//position_ = cVector(0, 15, 3); // direct edge drop (height = +5)
position_ = cVector(4, 8, 11); // double face collision (height = -5)
//position_ = cVector(1.5, 15, 2); // single face collision (height = -5)
//position_ = cVector(0, 10, 6); // vertex collision (height = +5)
//velocity_ = cVector(0, -2, 0);
//position_ = cVector(2, 10, 4); // vertex collision (height = +5)
//velocity_ = cVector(-1, -2, 1.5);
//position_ = cVector(0, 9, 8); // vertex collision (height = +5)
//velocity_ = cVector(0, -2, -1);
//position_ = cVector(4, 9, 2); // edge collision (height = +5)
//velocity_ = cVector(-2, -2, -1);
//position_ = cVector(15, 10, 7.00001); // edge collision (height = +5)
//velocity_ = cVector(-3, 0, 0);
//position_ = cVector(15, 10, 7.00001); // edge collision parallel to face (height = +5)
//velocity_ = cVector(-3, 0, 0);
}
*/
if(dt == 0) return; //nothing to do
cVector newPosition = position_ + dt * velocity_;
// the overall closest intersection
cIntersectionResult closestIntersection(false, 1); // no intersection yet
cVector normal; // the normal at the closest point of intersection
int numForwardFaces = 0; // the total number of faces contributing to the normal
// find closest face intersection and update the normal
for(int i = 0; i < staticObjects.numTriangles_; ++i)
{
cIntersectionResult result = intersectsFace(position_, newPosition, staticObjects.faces_[i]);
if(!result.intersected_) continue;
// in case we intersect two faces at the same time
if( abs(result.beta_ - closestIntersection.beta_) < .0001 )
{
normal = (normal * numForwardFaces + staticObjects.faces_[i].triangle_->getNormal());
normal.normalize();
++numForwardFaces;
}
else if( result.beta_ < closestIntersection.beta_ )
{
closestIntersection = result;
normal = staticObjects.faces_[i].triangle_->getNormal();
numForwardFaces = 1;
}
}
// find closest vertex intersection and update the normal
for(int i = 0; i < staticObjects.numVertices_; ++i)
{
cIntersectionResult result = intersectsVertex(position_, newPosition, staticObjects.vertices_[i]);
if(!result.intersected_) continue;
cVector newNormal;
int numNewForwardFaces = 0;
if(result.beta_ <= closestIntersection.beta_)
{
cVector incident = newPosition - position_;
numNewForwardFaces = staticObjects.vertices_[i].getAvgForwardFacingNormal(incident, newNormal);
}
// if there are 0 forward faces and we have intersected, it means
// that update has been called recursively after moving the ball
// so that it is exactly touching the vertex and now the ball's
// velocity is away from the vertex, so we should ignore it
if(numNewForwardFaces == 0 ) continue;
if(result.beta_ < closestIntersection.beta_)
{
numForwardFaces = numNewForwardFaces;
normal = newNormal;
closestIntersection = result;
}
if(result.beta_ == closestIntersection.beta_)
{
normal = numForwardFaces * normal + numNewForwardFaces * newNormal;
normal.normalize();
numForwardFaces += numNewForwardFaces;
closestIntersection = result;
}
}
// find closest edge intersection
for(int i = 0; i < staticObjects.numEdges_; ++i)
{
cIntersectionResult result = intersectsEdge(position_, newPosition, staticObjects.edges_[i]);
if(!result.intersected_) continue;
cVector newNormal;
int numNewForwardFaces = 0;
if(result.beta_ <= closestIntersection.beta_)
{
cVector incident = newPosition - position_;
numNewForwardFaces = staticObjects.edges_[i].getAvgForwardFacingNormal(incident, newNormal);
}
if(numNewForwardFaces == 0 ) continue;
if(result.beta_ < closestIntersection.beta_)
{
numForwardFaces = numNewForwardFaces;
normal = newNormal;
closestIntersection = result;
}
if(result.beta_ == closestIntersection.beta_)
{
normal = numForwardFaces * normal + numNewForwardFaces * newNormal;
normal.normalize();
numForwardFaces += numNewForwardFaces;
closestIntersection = result;
}
}
// there was no intersection with anything
if(closestIntersection.intersected_ == false)
{
position_ = newPosition;
velocity_ += cVector(0, -1, 0) * dt;
return;
}
position_ += closestIntersection.beta_ * dt * velocity_;
cVector reflectedVelocity = velocity_ - 2 * (normal.dot(velocity_)) * normal;
velocity_ = .7 * reflectedVelocity; // damping
update((1-closestIntersection.beta_) * dt, staticObjects);
// if the ball is not moving very fast, and it is touching something, dont add gravity
// later it will slide
if(closestIntersection.beta_ < .00001 && velocity_.length() < .00001) return;
velocity_ += cVector(0, -1, 0) * dt * closestIntersection.beta_; // add gravity
}
void cBall::draw()
{
glColor3f(1,1,1);
glPushMatrix();
glTranslatef(position_.x_,position_.y_,position_.z_);
// draw a sphere using 30 stacks and slices
glutSolidSphere(radius_,10,10);
glPopMatrix();
}
cIntersectionResult cBall::intersectsFace(cVector &start, cVector &end, cFace &face)
{
if(start.y_ == end.y_) return cIntersectionResult(false, 1);
// get the standardized triangle and
// transform the start and end vectors into standardized space
cStandardizedTriangle standard = face.triangle_->getStandardized();
float* transform = face.triangle_->getStandardVertexTranform();
cVector standardStart = start.matrixMultiply(transform);
cVector standardEnd = end.matrixMultiply(transform);
// no collision occurs if the ball is within distance radius_ from the xz plane
//if( standardStart.y_ < radius_ || standardStart.y_ <= standardEnd.y_ ) return cIntersectionResult(false, 1);
if(standardStart.y_ <= standardEnd.y_) return cIntersectionResult(false, 1);
// calculate beta such that the center of the ball is radius above the xz plane
float beta = (radius_ - standardStart.y_) / (standardEnd.y_ - standardStart.y_);
// if beta is not within [0,1] no intersection occurs
if( beta < 0 || 1 < beta ) return cIntersectionResult(false, 1);
// otherwise, compute projection of ball's position at y = radius onto the xz plane
cVector qPrime = standardStart + beta * (standardEnd - standardStart);
qPrime.y_ = 0;
// check the edge v2-v0
if( qPrime.z_ <= 0 ) return cIntersectionResult(false, 1); // check that z is positive
// check the edge v0-v1
if( (standard.v1_.x_ = 0) && (qPrime.x_ < 0) ) // if edge on the z-axis
return cIntersectionResult(false, 1);
if( qPrime.x_ <= (standard.v1_.x_ * (qPrime.z_ / standard.v1_.z_)) )
return cIntersectionResult(false, 1);
// check edge v1-v2
if( qPrime.x_ >= standard.v2_.x_ + (standard.v1_.x_ - standard.v2_.x_) * (qPrime.z_ - standard.v2_.z_) / (standard.v1_.z_ - standard.v2_.z_))
return cIntersectionResult(false, 1);
return cIntersectionResult(true, beta);
}
cIntersectionResult cBall::intersectsEdge(cVector &start, cVector &end, cEdge &edge)
{
// get the standardized edge and
// transform the start and end vectors into standardized space
float* transform = edge.getStandardEdgeTransform();
cVector standardStart = start.matrixMultiply(transform);
cVector standardEnd = end.matrixMultiply(transform);
// checking if velocity is parallel to the edge
if( standardStart.y_ = 0 && standardEnd.z_ = 0)
return cIntersectionResult(false,1);
float A = (standardEnd.y_- standardStart.y_)*(standardEnd.y_- standardStart.y_) + (standardEnd.z_-standardStart.z_)*(standardEnd.z_-standardStart.z_);
float B = 2 * ((standardStart.y_ * (standardEnd.y_- standardStart.y_)) + (start.z_ * (standardEnd.z_- standardStart.z_))) ;
float C = (standardStart.y_*standardStart.y_) + (standardStart.z_*standardStart.z_) - (radius_*radius_);
cIntersectionResult result = quadratic(A,B,C);
//checking to see if beta is actually on the edge not just x-axis
if( !result.intersected_)
return result;
if(standardStart.x_ + result.beta_* (standardEnd.x_ - standardStart.x_) < edge.standardizedX_ &&
standardStart.x_ + result.beta_* (standardEnd.x_ - standardStart.x_) > 0)
return result;
return cIntersectionResult(false, 1);
}
cIntersectionResult cBall::intersectsVertex(cVector &start, cVector &end, cVertex &vertex)
{
// the ball is moving toward the vertex, solve this quadratic
float A = (end.x_-start.x_)*(end.x_-start.x_) + (end.y_-start.y_)*(end.y_-start.y_) +
(end.z_-start.z_)*(end.z_-start.z_);
float B = 2*( (end.x_-start.x_)*(start.x_-vertex.x_) + (end.y_-start.y_)*(start.y_-vertex.y_) +
(end.z_-start.z_)*(start.z_-vertex.z_));
float C = (start.x_-vertex.x_)*(start.x_-vertex.x_)+(start.y_-vertex.y_)*(start.y_-vertex.y_)
+(start.z_-vertex.z_)*(start.z_-vertex.z_)- (radius_*radius_);
return quadratic(A,B,C);
}
cIntersectionResult cBall::quadratic(float A, float B, float C)
{
float discr = (B*B) - (4*A*C);
if( discr <= 0.0000001) return cIntersectionResult(false, 1);
discr = sqrt(discr);
float beta1 = (-1*B + discr)/ (2 * A);
float beta2 = (-1*B - discr)/(2 * A);
float minBeta = 1;
bool inRange = false;
if(beta2 > 0 && beta2 <= 1) { minBeta = beta2; inRange = true; }
if(beta1 > 0 && beta2 <= 1)
{
inRange = true;
if(beta1 < minBeta) minBeta = beta1;
}
if(inRange)
return cIntersectionResult(true, minBeta);
else return cIntersectionResult(false, 1);
} |