/***************************************************************************** * * Copyright (C) 2003 Cédric Brégardis * * This file is part of BRIQUOLO * * BRIQUOLO is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * BRIQUOLO is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with BRIQUOLO; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *****************************************************************************/ //#include #include #include "ElementCollision.h" #define DIST_COL 0.001 #define MAX_LOOP 50 #define DIST_MAX 3000 #define TEMPS_FIN_COLLISON 0.05 ElementCollision * ElementCollision::_DernierElementCollision=NULL; Balle * ElementCollision::_DerniereBalle=NULL; float ElementCollision::_DernierTempsCollision = 0; ElementCollision::ElementCollision(): ElementGraphique() { } ElementCollision::ElementCollision(const ElementCollision & p_ElementCollision) : ElementGraphique(p_ElementCollision) { } ElementCollision::~ElementCollision() { } void ElementCollision::SetDerniereCollision(ElementCollision * p_Element, Balle * p_Balle) { _DernierElementCollision=p_Element; _DerniereBalle=p_Balle; _DernierTempsCollision = MOGL_Temps::GetTemps(); } void ElementCollision::Deplacer(double p_Temps) { } bool ElementCollision::Toucher(Balle * p_Balle, double p_Temps) { return false; } Generateur * ElementCollision::CreerGenerateurParticules() { return NULL; } Struct_Collision ElementCollision::_CircleCollisionWithRotSearch(float c1ix, float c1iy, float r1, float v1x, float v1y, float brick_center_x, float brick_center_y, float brick_rot_i, float teta0, float ra, float r2, float v2x, float v2y, float v2r, float tmin, float dtmin, float tmax, float dtmax) { Struct_Collision col; _Niveau = _Niveau + 1; if (_Niveau < MAX_LOOP) { float t = (tmin + tmax) / 2; float teta = brick_rot_i + teta0 + t * v2r; float axt = ra * cos(teta) + brick_center_x; float ayt = ra * sin(teta) + brick_center_y; float bxt = c1ix + (v1x - v2x)*t; float byt = c1iy + (v1y - v2y)*t; // On calule la distance float dist = sqrt((bxt-axt)*(bxt-axt) + (byt-ayt)*(byt-ayt)); if (fabs(dist - (r1+r2)) < DIST_COL) { // On a la collision col.TempsCollision = t; col.PositionX = c1ix + v1x * t; col.PositionY = c1iy + v1y * t; col.VitesseX = -v1x; col.VitesseY = -v1y; return col; } else { // On a pas encore la collision if ((dist > dtmin) && (dist < (r1+r2))) { tmin = t; dtmin = dist; } else { tmax = t; dtmax = dist; } return _CircleCollisionWithRotSearch(c1ix, c1iy, r1, v1x, v1y, brick_center_x, brick_center_y, brick_rot_i, teta0, ra, r2, v2x, v2y, v2r, tmin, dtmin, tmax, dtmax); } } else { col.TempsCollision = -1; return col; } } Struct_Collision ElementCollision::_CircleCollisionWithRot(float c1ix, float c1iy, float r1, float v1x, float v1y, float brick_center_x, float brick_center_y, float brick_rot_i, float c2ix, float c2iy, float r2, float v2x, float v2y, float v2r, float tmax) { float teta0 = atan2(c2iy, c2ix); float ra = sqrt(c2ix*c2ix + c2iy*c2iy); // On regarde tmin float teta = brick_rot_i + teta0; float axt = ra * cos(teta) + brick_center_x; float ayt = ra * sin(teta) + brick_center_y; float bxt = c1ix; float byt = c1iy; float dist_min = sqrt((bxt-axt)*(bxt-axt) + (byt-ayt)*(byt-ayt)); // On regarde tmax teta = brick_rot_i + teta0 + tmax * v2r; axt = ra * cos(teta) + brick_center_x; ayt = ra * sin(teta) + brick_center_y; bxt = c1ix + (v1x - v2x)*tmax; byt = c1iy + (v1y - v2y)*tmax; float dist_max = sqrt((bxt-axt)*(bxt-axt) + (byt-ayt)*(byt-ayt)); Struct_Collision col; col.TempsCollision = -1; if (dist_min < (r1+r2) && dist_max > (r1+r2)) { col =_CircleCollisionWithRotSearch(c1ix, c1iy, r1, v1x, v1y, brick_center_x, brick_center_y, brick_rot_i, teta0, ra, r2, v2x, v2y, v2r, 0, dist_min, tmax, dist_max); } else if (dist_min > (r1+r2) && dist_max < (r1+r2)) { col =_CircleCollisionWithRotSearch(c1ix, c1iy, r1, v1x, v1y, brick_center_x, brick_center_y, brick_rot_i, teta0, ra, r2, v2x, v2y, v2r, tmax, dist_max, 0, dist_min); } if (col.TempsCollision == -1) { col.TempsCollision = tmax; } else { float new_vx, new_vy; float x = c1ix + col.TempsCollision*v1x; float y = c1iy + col.TempsCollision*v1y; teta = brick_rot_i + teta0 + col.TempsCollision * v2r; float bx = ra * cos(teta) + brick_center_x; float by = ra * sin(teta) + brick_center_y; //_Rebond(x-c2ix, y-c2iy, v1x, v1y, new_vx, new_vy); _Rebond(x-bx, y-by, v1x, v1y, new_vx, new_vy); col.PositionX = x; col.PositionY = y; col.VitesseX = new_vx; col.VitesseY = new_vy; } return col; } Struct_Collision ElementCollision::_CircleCollision(float c1ix, float c1iy, float r1, float v1x, float v1y, float brick_center_x, float brick_center_y, float brick_rot_i, float c2ix, float c2iy, float r2, float v2x, float v2y, float v2r, float tmax) { if (v2r == 0 && brick_rot_i == 0) { return _CircleCollisionWithoutRot(c1ix, c1iy, r1, v1x, v1y, brick_center_x, brick_center_y, c2ix, c2iy, r2, v2x, v2y, tmax); } else { return _CircleCollisionWithRot(c1ix, c1iy, r1, v1x, v1y, brick_center_x, brick_center_y, brick_rot_i, c2ix, c2iy, r2, v2x, v2y, v2r, tmax); } } Struct_Collision ElementCollision::_CircleCollisionWithoutRot(float c1ix, float c1iy, float r1, float v1x, float v1y, float brick_center_x, float brick_center_y, float c2ix, float c2iy, float r2, float v2x, float v2y, float tmax) { Struct_Collision col; c2ix += brick_center_x; c2iy += brick_center_y; float t, t2; float R = r1+r2; float Vx = v1x - v2x; float Vy = v1y - v2y; float a = ((R*R - c2ix*c2ix + 2*c1ix*c2ix - c1ix*c1ix)*Vy*Vy + ((2*c2ix-2*c1ix)*c2iy - 2*c1iy*c2ix + 2*c1ix*c1iy)*Vx*Vy + (R*R - c2iy*c2iy + 2*c1iy*c2iy - c1iy*c1iy)*Vx*Vx); if (a >= 0) { t = ((sqrt(a) + (c2iy-c1iy)*Vy + (c2ix-c1ix)*Vx)/ (Vy*Vy + Vx*Vx)); t2 = -((sqrt(a) + (c1iy-c2iy)*Vy + (c1ix-c2ix)*Vx)/ (Vy*Vy + Vx*Vx)); if (t<0 || t2>0 && t2 0) { float new_vx, new_vy; float x = c1ix + t*v1x; float y = c1iy + t*v1y; _Rebond(x-c2ix, y-c2iy, v1x, v1y, new_vx, new_vy); col.TempsCollision = t; col.PositionX = x; col.PositionY = y; col.VitesseX = new_vx; col.VitesseY = new_vy; return col; } else { col.TempsCollision = tmax; return col; } } else { col.TempsCollision = tmax; return col; } } Struct_Collision ElementCollision::_SegmentCollision(float c1ix, float c1iy, float r1, float v1x, float v1y, float brick_center_x, float brick_center_y, float brick_rot_i, float teta0, float ra, float gama0, float rb, float vtx, float vty, float vr, float tmin, float dtmin, float detmin, float tmax, float dtmax, float detmax) { Struct_Collision col; _Niveau = _Niveau + 1; if (_Niveau < MAX_LOOP) { float t = (tmin + tmax) / 2; float teta = brick_rot_i + teta0 + t * vr; float axt = ra * cos(teta) + brick_center_x; float ayt = ra * sin(teta) + brick_center_y; float gama = brick_rot_i + gama0 + t * vr; float bxt = rb * cos(gama) + brick_center_x; float byt = rb * sin(gama) + brick_center_y; float a = byt - ayt; float b = axt - bxt; float c = ayt*bxt - axt*byt; float xi = c1ix + v1x*t - vtx*t; float yi = c1iy + v1y*t - vty*t; // On calcul le vecteur normal à la droite float n = sqrt(a*a + b*b); float nx = a/n; float ny = b/n; float xit, yit; // On effectue la translation correspondant au rayon if (detmin > 0) { xit = xi - nx * r1; yit = yi - ny * r1; } else { xit = xi + nx * r1; yit = yi + ny * r1; } float dist2 = fabs((a*xit+b*yit+c)*(a*xit+b*yit+c) / (a*a + b*b)) ; if (dist2 < DIST_COL) { float k1 = 0.5; if (fabs(bxt - axt) > 0.0001) { k1 = (xit - axt) / (bxt - axt); } float k2 = 0.5; if (fabs(byt - ayt) > 0.0001) { k2 = (yit - ayt) / (byt - ayt); } if (k1>=0 && k1<=1 && k2>=0 && k2<=1) { float new_vx, new_vy; _Rebond(a, b, v1x, v1y, new_vx, new_vy); col.TempsCollision = t; col.PositionX = xi; col.PositionY = yi; col.VitesseX = new_vx; col.VitesseY = new_vy; return col; } else { col.TempsCollision = -1; return col; } } else { float detc = _Determinant(xit - axt, yit - ayt, bxt - axt, byt - ayt); if (detc*detmin > 0) { return _SegmentCollision(c1ix, c1iy, r1, v1x, v1y, brick_center_x, brick_center_y, brick_rot_i, teta0, ra, gama0, rb, vtx, vty, vr, t, dist2, detc, tmax, dtmax, detmax); } else { return _SegmentCollision(c1ix, c1iy, r1, v1x, v1y, brick_center_x, brick_center_y, brick_rot_i, teta0, ra, gama0, rb, vtx, vty, vr, tmin, dtmin, detmin, t, dist2, detc); } } } else { col.TempsCollision = -1; return col; } } Struct_Collision ElementCollision::_SegmentCollision(float c1ix, float c1iy, float r1, float v1x, float v1y, float brick_center_x, float brick_center_y, float brick_rot_i, float ax, float ay, float bx, float by, float vtx, float vty, float vr, float tmin, float tmax) { Struct_Collision col; col.TempsCollision = tmax; _Niveau = 0; float teta0 = atan2(ay, ax); float ra = sqrt(ax*ax + ay*ay); float gama0 = atan2(by, bx); float rb = sqrt(bx*bx + by*by); // ai float teta = brick_rot_i + teta0 + tmin * vr; float axi = ra * cos(teta) + brick_center_x; float ayi = ra * sin(teta) + brick_center_y; // bi float gama = brick_rot_i + gama0 + tmin * vr; float bxi = rb * cos(gama) + brick_center_x; float byi = rb * sin(gama) + brick_center_y; // af teta = brick_rot_i + teta0 + tmax * vr; float axf = ra * cos(teta) + brick_center_x; float ayf = ra * sin(teta) + brick_center_y; // bf gama = brick_rot_i + gama0 + tmax * vr; float bxf = rb * cos(gama) + brick_center_x; float byf = rb * sin(gama) + brick_center_y; // **** Premier coté // ** Debut float a = byi - ayi; float b = axi - bxi; float c = ayi*bxi - axi*byi; // On calcul le vecteur normal à la droite float n = sqrt(a*a + b*b); float nx = a/n; float ny = b/n; // ci float cxi = c1ix + tmin*(v1x - vtx) - nx * r1; float cyi = c1iy + tmin*(v1y - vty) - ny * r1; // ** Fin a = byf - ayf; b = axf - bxf; c = ayf*bxf - axf*byf; // On calcul le vecteur normal à la droite n = sqrt(a*a + b*b); nx = a/n; ny = b/n; // cf float cxf = c1ix + tmax*(v1x - vtx) - nx * r1; float cyf = c1iy + tmax*(v1y - vty) - ny * r1; float deti = _Determinant(cxi - axi, cyi - ayi, bxi - axi, byi - ayi); float detf = _Determinant(cxf - axf, cyf - ayf, bxf - axf, byf - ayf); if (deti*detf < 0) { col = _SegmentCollision(c1ix, c1iy, r1, v1x, v1y, brick_center_x, brick_center_y, brick_rot_i, teta0, ra, gama0, rb, vtx, vty, vr, tmin, DIST_MAX, deti, tmax, DIST_MAX, detf); if (col.TempsCollision == -1) { col.TempsCollision = tmax; } return col; } // **** Deuxième coté // ** Debut a = byi - ayi; b = axi - bxi; c = ayi*bxi - axi*byi; // On calcul le vecteur normal à la droite n = sqrt(a*a + b*b); nx = a/n; ny = b/n; // ci cxi = c1ix + tmin*(v1x - vtx) + nx * r1; cyi = c1iy + tmin*(v1y - vty) + ny * r1; // ** Fin a = byf - ayf; b = axf - bxf; c = ayf*bxf - axf*byf; // On calcul le vecteur normal à la droite n = sqrt(a*a + b*b); nx = a/n; ny = b/n; // cf cxf = c1ix + tmax*(v1x - vtx) + nx * r1; cyf = c1iy + tmax*(v1y - vty) + ny * r1; deti = _Determinant(cxi - axi, cyi - ayi, bxi - axi, byi - ayi); detf = _Determinant(cxf - axf, cyf - ayf, bxf - axf, byf - ayf); if (deti*detf < 0) { Struct_Collision col2 = _SegmentCollision(c1ix, c1iy, r1, v1x, v1y, brick_center_x, brick_center_y, brick_rot_i, teta0, ra, gama0, rb, vtx, vty, vr, tmin, DIST_MAX, deti, tmax, DIST_MAX, detf); if (col2.TempsCollision == -1) { col2.TempsCollision = tmax; } if (col2.TempsCollision < col.TempsCollision) { col = col2; } } return col; } float ElementCollision::_Determinant(float d1x, float d1y, float d2x, float d2y) { return d1x*d2y - d1y*d2x; } void ElementCollision::_Rebond(float nx, float ny, float vix, float viy, float & vfx, float & vfy) { float vit=sqrt(vix*vix + viy*viy); float beta=atan2(viy, vix); float gama=atan2(ny , nx); vfx=-vit*cos(2*gama-beta); vfy=-vit*sin(2*gama-beta); } string ElementCollision::GetLigneOption() const { return _LigneOption; } void ElementCollision::SetLigneOption(const string & p_Ligne) { _LigneOption = p_Ligne; _AnalyserLigne(); } void ElementCollision::_AnalyserLigne() { unsigned int posi, posf = 0; _Params.clear(); while (posf < _LigneOption.size()) { posi = posf; while(posf < _LigneOption.size() && _LigneOption[posf] != '=' && _LigneOption[posf] != ' ' && _LigneOption[posf] != '\t' && _LigneOption[posf] != '\n') { posf++; } if ((posf < _LigneOption.size()) && (_LigneOption[posf] == '=') && (posf > posi)) { string param = _LigneOption.substr(posi, posf - posi); posi = ++posf; while(posf < _LigneOption.size() && _LigneOption[posf] != ' ' && _LigneOption[posf] != '\t' && _LigneOption[posf] != '\n') { posf++; } if (posf > posi) { string value = _LigneOption.substr(posi, posf - posi); _Params[param] = value; } } posf++; } } Point ElementCollision::GetVisualCenter() { return Point(_X, _Y); } float ElementCollision::_Modulo(float a, float b) { float f = fmodf(a, b); if (f < 0) { f = 360 + f; } return f; } bool ElementCollision::IsDerniereCollisionOk(Balle * p_Balle) { /* if (p_Balle == _DerniereBalle && _DernierElementCollision == this) { if (_DernierTempsCollision + TEMPS_FIN_COLLISON < MOGL_Temps::GetTemps()) { _DernierTempsCollision = NULL; return true; } else { return false; } } return true; */ if (p_Balle == _DerniereBalle && _DernierElementCollision == this) { return false; } else { return true; } }