/* * $Id: collision.cpp,v 1.24 2002/07/21 15:03:12 msell Exp $ * * * $Log: collision.cpp,v $ * Revision 1.24 2002/07/21 15:03:12 msell * Äänet disabloitu * * Revision 1.23 2002/07/19 18:59:46 msell * Alkuhommaa ja säätöä * * Revision 1.22 2002/07/18 23:05:31 msell * Partikkelit ja kakkospelaajan liike * * Revision 1.21 2002/07/17 22:45:53 msell * Ääniä vähän * * Revision 1.20 2002/07/15 15:22:07 msell * Parantelua * * Revision 1.19 2002/07/14 21:40:43 msell * Conflictit pois, liikkumiset (hyppy, kävely, lyönti), uusi areena * * Revision 1.18 2002/07/10 17:13:44 msell * Törmäystarkastelun parantelua * * Revision 1.17 2002/07/08 22:53:38 msell * Säätöä * * Revision 1.16 2002/07/08 18:28:47 msell * Törmäystä ja ukkoja * * Revision 1.15 2002/07/07 23:21:11 msell * Pientä parantelua * * Revision 1.14 2002/07/07 23:05:22 msell * Osien liimaaminen toisiinsa (kesken) * * Revision 1.13 2002/07/07 15:29:07 msell * Törmäyksien parantelua * * Revision 1.12 2002/07/04 21:05:41 msell * Se toimii!! =) * Törmäystarkistukset siis * * Revision 1.11 2002/06/30 16:05:04 msell * Törmäyksien parantelua, transformaatioita mukana * * Revision 1.10 2002/06/27 14:39:48 msell * Toimiva maila :) * Pyörivät kappaleet siis antaa liike-energiaa liikkuville kappaleille (ei toisin päin vielä) * * Revision 1.9 2002/06/27 00:08:04 msell * Kimmotukset palloille myös pyöritettyihin mesheihin * * Revision 1.8 2002/06/23 20:12:19 msell * Parempi törmäystarkistus palloista mesheihin * * Revision 1.7 2002/06/20 22:50:12 msell * Meshit * * Revision 1.6 2002/06/17 12:42:46 msell * Hieman parempi törmäysmallinnus taas * * Revision 1.5 2002/06/15 22:56:37 msell * Säätöä * * Revision 1.4 2002/06/15 17:18:37 msell * Toimiva törmäystarkastus kiinteille laatikoille * * Revision 1.3 2002/06/14 00:26:17 msell * 100 kimpoilevaa palloa ja vähän vaimennusta * * Revision 1.2 2002/06/14 00:05:05 msell * Törmäyssimulaatio kunnossa toivon mukaan * * Revision 1.1 2002/06/11 23:11:45 msell * Törmäystarkistusta * * * * $Date: 2002/07/21 15:03:12 $ * */ #include #include #include "object.h" #include "vector.h" #include "collision.h" #include "mesh.h" #include "3dutils.h" #include "world.h" #include "audio.h" //objectlist *collisionlists[32]; unsigned int collisionlinks[32]; Contact *contacts; int contactcount; void initCollisions(void){ int i; for (i = 0; i < 32; i++){ //collisionlists[i] = NULL; collisionlinks[i] = 0; } } /*void addCollisionObject(Object *object, int group){ objectlist *node = new objectlist; node->object = object; node->next = collisionlists[group]; collisionlists[group] = node; }*/ void addCollisionLink(int source, int target){ collisionlinks[source] |= (1<object1 = source; contact->object2 = target; vectorCopy(contact->normal, normal); vectorCopy(contact->position, contactpoint); } #define KINETICFRICTION 0.4 bool handleCollision(Contact *contact){ Object *source = contact->object1; Object *target = contact->object2; float *normal = contact->normal; float *contactpoint = contact->position; float sourcevelocity[3], targetvelocity[3]; float sourcecontactpoint[3], targetcontactpoint[3]; vectorSub(sourcecontactpoint, contactpoint, source->position); source->getVelocity(sourcevelocity, sourcecontactpoint); if (target == NULL){ vectorSet(targetcontactpoint, 0, 0, 0); vectorSet(targetvelocity, 0, 0, 0); } else{ vectorSub(targetcontactpoint, contactpoint, target->position); target->getVelocity(targetvelocity, targetcontactpoint); } float deltavelocity[3]; vectorSub(deltavelocity, sourcevelocity, targetvelocity); float dot = vectorDot(deltavelocity, normal); //if (fabs(dot) < EPSILON) return false; //if (dot > -1.0e-5 && dot < 1.0e-5) return false; //if (dot >= 0) return false; if (dot > -1.0e-5) return false; float invmass1; invmass1 = source->invmass; float invmass2; if (target == NULL) invmass2 = 0; else invmass2 = target->invmass; float t1; if (source->invmomentofinertia == 0){ t1 = 0; } else{ float v1[3]; vectorCross(v1, sourcecontactpoint, normal); vectorScale(v1, source->invmomentofinertia); float w1[3]; vectorCross(w1, v1, sourcecontactpoint); t1 = vectorDot(normal, w1); } float t2; if (target == NULL || target->invmomentofinertia == 0){ t2 = 0; } else{ float v1[3]; vectorCross(v1, targetcontactpoint, normal); vectorScale(v1, target->invmomentofinertia); float w1[3]; vectorCross(w1, v1, targetcontactpoint); t2 = vectorDot(normal, w1); } float denominator = invmass1 + invmass2 + t1 + t2; float e = 1.0 - COLLISIONFRICTION; float impulsesize = (1 + e) * dot / denominator; //printf("%f\n", impulsesize); float impulse[3]; vectorScale(impulse, normal, impulsesize); float friction[3]; vectorScale(friction, normal, vectorDot(deltavelocity, normal)); vectorAdd(friction, deltavelocity); vectorNormalize(friction); float frictionsize = 10*KINETICFRICTION*dot/denominator; float maxfrictionsize = 0.1*vectorLength(deltavelocity); if (frictionsize < -maxfrictionsize) frictionsize = -maxfrictionsize; vectorScale(friction, -frictionsize); vectorAdd(impulse, friction); if (target != NULL){ target->addImpulse(impulse, targetcontactpoint); target->calculateStateVariables(); } float speed; float speed2[3]; if (target != NULL && source != NULL){ //float kvel[3]; //source->getVelocity(kvel); float k = vectorLength(sourcevelocity)*0.1; //if (k > 1) k = 1; speed = -impulsesize*target->invmass*k; vectorScale(speed2, impulse, target->invmass*k); /*float kvel[3]; source->getVelocity(kvel); float k = 0;//vectorDot(speed2, kvel); if (k < EPSILON) k = 0; speed *= k; vectorScale(speed2, k); if (k > 0) */target->hitForce(speed, speed2, source); } vectorScale(impulse, -1); source->addImpulse(impulse, sourcecontactpoint); source->calculateStateVariables(); //vectorScale(speed, source->invmass); if (target != NULL && source != NULL){ //float kvel[3]; //target->getVelocity(kvel); float k = vectorLength(targetvelocity)*0.1; //if (k > 1) k = 1; speed = -impulsesize*source->invmass*k; vectorScale(speed2, impulse, source->invmass*k); /*float kvel[3]; target->getVelocity(kvel); float k = 0;//vectorDot(speed2, kvel); if (k < EPSILON) k = 0; speed *= k; vectorScale(speed2, k); if (k > 0) */source->hitForce(speed, speed2, target); } return true; } bool handleLink(ObjectLink *link){ if (!link->enabled) return false; Object *source = link->object1; Object *target = link->object2; float normal[3]; float contactpoint1[3], contactpoint2[3]; source->transformPoint(contactpoint1, link->point1); target->transformPoint(contactpoint2, link->point2); float diff[3]; vectorSub(diff, contactpoint1, contactpoint2); vectorNormalize(normal, diff); float strength = vectorDot(diff, diff); if (strength < 1.0e-5) return false; float sourcevelocity[3], targetvelocity[3]; float sourcecontactpoint[3], targetcontactpoint[3]; vectorSub(sourcecontactpoint, contactpoint1, source->position); source->getVelocity(sourcevelocity, sourcecontactpoint); vectorSub(targetcontactpoint, contactpoint2, target->position); target->getVelocity(targetvelocity, targetcontactpoint); float deltavelocity[3]; vectorSub(deltavelocity, sourcevelocity, targetvelocity); float dot = vectorDot(deltavelocity, normal); //if (fabs(dot) < EPSILON) return false; //if (dot > -1.0e-5 && dot < 1.0e-5) return false; //if (dot >= 0) return false; //if (dot > -1.0e-5) return false; float invmass1 = source->invmass; float invmass2 = target->invmass; float t1; if (source->invmomentofinertia == 0){ t1 = 0; } else{ float v1[3]; vectorCross(v1, sourcecontactpoint, normal); vectorScale(v1, source->invmomentofinertia); float w1[3]; vectorCross(w1, v1, sourcecontactpoint); t1 = vectorDot(normal, w1); } float t2; if (target->invmomentofinertia == 0){ t2 = 0; } else{ float v1[3]; vectorCross(v1, targetcontactpoint, normal); vectorScale(v1, target->invmomentofinertia); float w1[3]; vectorCross(w1, v1, targetcontactpoint); t2 = vectorDot(normal, w1); } float denominator = invmass1 + invmass2 + t1 + t2; float impulsesize = (dot + strength*100) / denominator; //printf("%f\n", impulsesize); float impulse[3]; vectorScale(impulse, normal, impulsesize); target->addImpulse(impulse, targetcontactpoint); target->calculateStateVariables(); vectorScale(impulse, -1); source->addImpulse(impulse, sourcecontactpoint); source->calculateStateVariables(); return true; } bool checkCollisions(Object *object, float *contactnormal){ //bool collision = false; int group = object->getCollisionGroup(); int groups = collisionlinks[group]; group = 0; int collisions = 0; /*float oldmomentum[3]; vectorCopy(oldmomentum, object->momentum); while (groups){ if (groups & 1){ objectlist *node = collisionlists[group]; while (node != NULL){ Object *object2 = node->object; node = node->next; if (object != object2){ if (object2->geometry->checkCollision(object, contactnormal)){ collisions++; } } } } group++; groups >>= 1; } /*float temp[3]; vectorSub(temp, object->momentum, oldmomentum); vectorScale(temp, 1.0/DT); object->addForce(temp);*/ //vectorSub(object-> //vectorCopy(object->momentum, oldmomentum); //printf("%i\n", collisions); //vectorScale(object->force, 1.0/collisions); /*if (collisions > 0){ vectorScale(object->force, 2.0/vectorLength(object->momentum)); }*/ return collisions > 0; } /* * mass = 0 means infinite mass */ /*void collide(float *velocity1, float *velocity2, float mass1, float mass2, float *normal, float *newimpulse){ float deltavelocity[3]; vectorSub(deltavelocity, velocity1, velocity2); float dot = vectorDot(deltavelocity, normal); float massinverse1 = 0; float massinverse2 = 0; if (mass1 != 0) massinverse1 = 1.0 / mass1; if (mass2 != 0) massinverse2 = 1.0 / mass2; float impulsesize = -1 * dot / (massinverse1 + massinverse2); vectorScale(newimpulse, normal, impulsesize); if (dot > 0){ vectorSet(newimpulse, 0, 0, 0); } }*/ void collide(Object *source, Object *target, float *normal, float *contactpoint){ /* float momentum[3]; source->getMomentum(momentum); float sourcevelocity[3], targetvelocity[3]; float sourcecontactpoint[3], targetcontactpoint[3]; //source->unTransformPoint(sourcecontactpoint, contactpoint); //target->unTransformPoint(targetcontactpoint, contactpoint); vectorSub(sourcecontactpoint, contactpoint, source->position); vectorSub(targetcontactpoint, contactpoint, target->position); source->getVelocity(sourcevelocity);//, sourcecontactpoint); float sourcemass = source->getMass(); target->getVelocity(targetvelocity);//, targetcontactpoint); float targetmass = target->getMass(); float deltavelocity[3]; vectorSub(deltavelocity, sourcevelocity, targetvelocity); float dot = vectorDot(deltavelocity, normal); float massinverse1 = 0; float massinverse2 = 0; if (sourcemass != 0) massinverse1 = 1.0 / sourcemass; if (targetmass != 0) massinverse2 = 1.0 / targetmass; float t1; if (source->invmomentofinertia == 0){ t1 = 0; } else{ float v1[3]; vectorCross(v1, sourcecontactpoint, normal); vectorScale(v1, 1.0/source->momentofinertia); float w1[3]; vectorCross(w1, v1, sourcecontactpoint); t1 = vectorDot(normal, w1); } //printf("S: %f, %f, %f\n", sourcecontactpoint[0], source->momentofinertia, v1); float t2; if (target->momentofinertia == 0){ t2 = 0; } else{ float v2[3]; vectorCross(v2, targetcontactpoint, normal); vectorScale(v2, 1.0/target->momentofinertia); float w2[3]; vectorCross(w2, v2, targetcontactpoint); t2 = vectorDot(normal, w2); } //printf("T: %f, %f, %f\n", targetcontactpoint[0], target->momentofinertia, v2); float divisor; divisor = massinverse1 + massinverse2;// + t1 + t2; printf("%f, %f, %f, %f : %f\n", massinverse1, massinverse2, t1, t2, divisor); float impulsesize = -dot / divisor; float impulse[3]; vectorScale(impulse, normal, impulsesize); //collide(sourcevelocity, targetvelocity, sourcemass, targetmass, normal, impulse); //vectorAdd(source->momentum, impulse); float *force = impulse; //vectorScale(force, 2.0/DT); printf("F: %f, %f, %f\n", impulse[0], impulse[1], impulse[2]); //float cp[3]; //vectorSub(cp, contactpoint, source->position); //source->addForce(force, sourcecontactpoint);*/ } bool checkSphereMeshCollision(float *sphereposition, float r, Mesh *mesh, float *normal, float *contactpoint){ float linenormal[3]; float pointnormal[3]; float maxdist = 0; bool planecollision = false; bool linecollision = false; bool pointcollision = false; int i, j; for (i = 0; i < mesh->polygoncount; i++){ class Polygon *polygon = &mesh->polygons[i]; float dist = distanceFromPlane(sphereposition, polygon->planenormal, polygon->planedistance); if (dist < r && dist > -r){ bool directcollision = true; for (j = 0; j < polygon->vertexcount; j++){ float *p1 = polygon->vertices[j]->position; float *p2 = polygon->vertices[(j+1)%polygon->vertexcount]->position; float *p3 = polygon->vertices[(j+2)%polygon->vertexcount]->position; float v1[3], v2[3]; vectorSub(v1, p2, p1); //Collision for polygon surface vectorSub(v2, p3, p2); float t1[3]; vectorProject(t1, v2, v1); float norm[3]; vectorSub(norm, v2, t1); vectorNormalize(norm); //Collision for polygon edges float newpoint[3]; vectorSub(newpoint, sphereposition, p1); float dist2 = vectorDot(newpoint, norm); if (dist2 < 0){ directcollision = false; float projloc = vectorDot(newpoint, v1) / vectorDot(v1, v1); if (projloc >= 0 && projloc <= 1){ float proj[3]; vectorScale(proj, v1, projloc); float projorth[3]; vectorSub(projorth, newpoint, proj); float l2 = vectorDot(projorth, projorth); if (l2 < r*r){ vectorNormalize(linenormal, projorth); if (dist < 0) vectorScale(linenormal, -1); linecollision = true; } } } //Collision for polygon vertices float pointdiff[3]; vectorSub(pointdiff, sphereposition, p1); float l3 = vectorDot(pointdiff, pointdiff); if (l3 < r*r){ vectorScale(pointnormal, pointdiff, 1.0 / sqrt(l3)); if (dist < 0) vectorScale(pointnormal, -1); pointcollision = true; } } if (directcollision){ if (dist > maxdist || !planecollision){ vectorCopy(normal, polygon->planenormal); maxdist = dist; planecollision = true; } } } } if (planecollision){ vectorScale(contactpoint, normal, -r); vectorAdd(contactpoint, sphereposition); } else if (linecollision){ vectorScale(contactpoint, linenormal, -r); vectorAdd(contactpoint, sphereposition); vectorCopy(normal, linenormal); } else if (pointcollision){ vectorScale(contactpoint, pointnormal, -r); vectorAdd(contactpoint, sphereposition); vectorCopy(normal, pointnormal); } else{ return false; } return true; } bool checkPointMeshCollision(float *position, Mesh *mesh, float *normal, float *contactpoint){ float maxdist = 0; bool planecollision = false; int i; for (i = 0; i < mesh->polygoncount; i++){ class Polygon *polygon = &mesh->polygons[i]; float dist = distanceFromPlane(position, polygon->planenormal, polygon->planedistance); if (dist < 0){ bool directcollision = true; /*for (j = 0; j < polygon->vertexcount; j++){ float *p1 = polygon->vertices[j]->position; float *p2 = polygon->vertices[(j+1)%polygon->vertexcount]->position; float *p3 = polygon->vertices[(j+2)%polygon->vertexcount]->position; float v1[3], v2[3]; vectorSub(v1, p2, p1); //Collision for polygon surface vectorSub(v2, p3, p2); float t1[3]; vectorProject(t1, v2, v1); float norm[3]; vectorSub(norm, v2, t1); vectorNormalize(norm); //Collision for polygon edges float newpoint[3]; vectorSub(newpoint, position, p1); float dist2 = vectorDot(newpoint, norm); if (dist2 < 0) directcollision = false; }*/ if (directcollision){ if (dist > maxdist || !planecollision){ vectorCopy(normal, polygon->planenormal); maxdist = dist; planecollision = true; } } } else{ return false; } } if (planecollision){ vectorCopy(contactpoint, position); } else{ return false; } return true; } #define MAXPOLYGONS 1000 struct tracehit{ float t; Polygon *polygon; }; bool tracePlane(tracehit *result, float *origin, float *ray, class Polygon *polygon){ float *normal = polygon->planenormal; float D = polygon->planedistance; float denominator = vectorDot(normal, ray); if (denominator == 0){ if (vectorDot(normal, origin) > 0) result->t = 1000000; else result->t = -1000000; result->polygon = polygon; return true; } float t = - (vectorDot(normal, origin) + D) / denominator; result->t = t; result->polygon = polygon; //if (t < 0 || t > 1) return false; return true; } Edge *findSharingEdge(class Polygon *p1, class Polygon *p2){ //printf("Edges:\n"); int i, j; for (i = 0; i < p1->vertexcount; i++){ //printf("%p\n", p1->edges[i]); for (j = 0; j < p2->vertexcount; j++){ if (p1->edges[i] == p2->edges[j]) return p1->edges[i]; } } return NULL; } /*Polygon *findSharingPolygon(Mesh *mesh, class Polygon *p1, class Polygon *p2){ //printf("Edges:\n"); int i, j; for (i = 0; i < p1->vertexcount; i++){ //printf("%p\n", p1->edges[i]); for (j = 0; j < p2->vertexcount; j++){ if (p1->edges[i] == p2->edges[j]) return p1->edges[i]; } } return NULL; }*/ Polygon *findNearestPolygon(class Polygon *polygon, float *point){ int i; float mindist = 0; Polygon *nearestpolygon = NULL; for (i = 0; i < polygon->edgecount; i++){ Edge *edge = polygon->edges[i]; Polygon *polygon2 = edge->p1; if (polygon2 == polygon) polygon2 = edge->p2; float newdist = distanceFromPlane(point, polygon2->planenormal, polygon2->planedistance); if (newdist > 0) return NULL; if (mindist == 0 || newdist > mindist){ mindist = newdist; nearestpolygon = polygon2; } } return nearestpolygon; } bool checkEdgeMeshCollision(float *p1, float *p2, Mesh *mesh, float *normal, float *contactpoint){ float ray[3]; vectorSub(ray, p2, p1); float maxdist = 0; bool collision = false; int i, j; tracehit hits[MAXPOLYGONS]; int hitcount = 0; for (i = 0; i < mesh->polygoncount; i++){ class Polygon *polygon = &mesh->polygons[i]; if (tracePlane(&hits[hitcount], p1, ray, polygon)){ hitcount++; } } if (hitcount < 2) return false; for (i = 1; i < hitcount; i++){ for (j = i; j > 0; j--){ if (hits[j].t < hits[j-1].t){ float tempt = hits[j].t; hits[j].t = hits[j-1].t; hits[j-1].t = tempt; class Polygon *tempp = hits[j].polygon; hits[j].polygon = hits[j-1].polygon; hits[j-1].polygon = tempp; } else break; } } int negative = -1, positive = -1; for (i = 0; i < hitcount; i++){ float t = hits[i].t; class Polygon *polygon = hits[i].polygon; float dot = vectorDot(ray, polygon->planenormal); if (dot > 0 && positive == -1) positive = i; if (dot < 0) negative = i; if (dot < 0 && positive != -1) return false; } if (negative == -1 || positive == -1) return false; /*for (i = 0; i < hitcount; i++){ float t = hits[i].t; class Polygon *polygon = hits[i].polygon; float dot = vectorDot(ray, polygon->planenormal); printf("%f ", dot); } printf("\n");*/ if (hits[negative].t < 0 || hits[positive].t > 1) return false; Edge *edge2 = findSharingEdge(hits[negative].polygon, hits[positive].polygon); //fflush(stdout); float cp1[3], cp2[3]; vectorScale(cp1, ray, hits[negative].t); vectorAdd(cp1, p1); vectorScale(cp2, ray, hits[positive].t); vectorAdd(cp2, p1); if (edge2 != NULL){ /*float ev1[3]; vectorSub(ev1, edge2->v2->position, edge2->v1->position); vectorCross(normal, ev1, ray); vectorScale(normal, vectorDot(normal, hits[positive].polygon->planenormal)); vectorNormalize(normal); float at = (hits[negative].t + hits[positive].t) / 2; vectorScale(contactpoint, ray, at); vectorAdd(contactpoint, p1);*/ float dot1 = fabs(vectorDot(ray, hits[negative].polygon->planenormal)); float dot2 = fabs(vectorDot(ray, hits[positive].polygon->planenormal)); if (dot1 > dot2){ //vectorScale(contactpoint, ray, hits[negative].t); //vectorAdd(contactpoint, p1); vectorCopy(contactpoint, cp1); vectorCopy(normal, hits[positive].polygon->planenormal); } else{ //vectorScale(contactpoint, ray, hits[positive].t); //vectorAdd(contactpoint, p1); vectorCopy(contactpoint, cp2); vectorCopy(normal, hits[negative].polygon->planenormal); } } else{ Polygon *polygon = findNearestPolygon(hits[negative].polygon, cp1); if (polygon != NULL){ /*vectorCopy(contactpoint, cp1); vectorAdd(contactpoint, cp2); vectorScale(contactpoint, 0.5);*/ float at = (hits[negative].t + hits[positive].t) / 2; vectorScale(contactpoint, ray, at); vectorAdd(contactpoint, p1); vectorCopy(normal, polygon->planenormal); } else{ return false; } } //shotsound->play(); return true; }