274 lines
7.5 KiB
C
274 lines
7.5 KiB
C
#include "lephisto.h"
|
|
#include "log.h"
|
|
#include "collision.h"
|
|
|
|
/**
|
|
*
|
|
* @fn CollideSprite(const glTexture* at, const int asx, const int asy,
|
|
* const Vec2* ap, const glTexture* bt,
|
|
* const int bsx, const int bsy, const Vec2* bp,
|
|
* Vec2* crash)
|
|
*
|
|
* @brief Check whether or not sprites collide.
|
|
*
|
|
* This function does pixel perfect checks. If the collision actually accurs,
|
|
* crash is set to store the real position of the collision.
|
|
*
|
|
* @param[in] at - Texture a.
|
|
* @param[in] asx - Position of x of sprite a.
|
|
* @param[in] asy - Position of y of sprite a.
|
|
* @param[in] ap - Position in space of sprite a.
|
|
* @param[in] bt - Texture b.
|
|
* @param[in] bsx - Position of x of sprite b.
|
|
* @param[in] bsy - Position of y of sprite b.
|
|
* @param[in] bp - Position in space of sprite b.
|
|
* @param[out] crash - Actual position of the collision (only on collision).
|
|
* @return 1 on collision, 0 else.
|
|
*/
|
|
int CollideSprite(const glTexture* at, const int asx, const int asy,
|
|
const Vec2* ap, const glTexture* bt,
|
|
const int bsx, const int bsy, const Vec2* bp,
|
|
Vec2* crash) {
|
|
|
|
int x,y;
|
|
int ax1, ax2, ay1, ay2;
|
|
int bx1, bx2, by1, by2;
|
|
int inter_x0, inter_x1, inter_y0, inter_y1;
|
|
int rasy, rbsy;
|
|
int abx, aby, bbx, bby;
|
|
|
|
/* a - cube coords. */
|
|
ax1 = (int)VX(*ap) - (int)(at->sw)/2;
|
|
ay1 = (int)VY(*ap) - (int)(at->sh)/2;
|
|
ax2 = ax1 + (int)(at->sw) - 1;
|
|
ay2 = ay1 + (int)(at->sh) - 1;
|
|
|
|
/* b - cube coords. */
|
|
bx1 = (int)VX(*bp) - (int)(bt->sw)/2;
|
|
by1 = (int)VY(*bp) - (int)(bt->sh)/2;
|
|
bx2 = bx1 + (int)(bt->sw) - 1;
|
|
by2 = by1 + (int)(bt->sh) - 1;
|
|
|
|
/* Check if bounding boxes intersect. */
|
|
if((bx2 < ax1) || (ax2 < bx1)) return 0;
|
|
if((by2 < ay1) || (ay2 < by1)) return 0;
|
|
|
|
/* Define the remaining binding box. */
|
|
inter_x0 = MAX(ax1, bx1);
|
|
inter_x1 = MIN(ax2, bx2);
|
|
inter_y0 = MAX(ay1, by1);
|
|
inter_y1 = MIN(ay2, by2);
|
|
|
|
/* Real vertical sprite value (flipped). */
|
|
rasy = at->sy - asy - 1;
|
|
rbsy = bt->sy - bsy - 1;
|
|
|
|
/* Set up the base points. */
|
|
abx = asx*(int)(at->sw) - ax1;
|
|
aby = rasy*(int)(at->sh) - ay1;
|
|
bbx = bsx*(int)(bt->sw) - bx1;
|
|
bby = rbsy*(int)(bt->sh) - by1;
|
|
|
|
for(y = inter_y0; y <= inter_y1; y++)
|
|
for(x = inter_x0; x <= inter_x1; x++)
|
|
/* Compute offsets for surface before passing to TransparentPixel test. */
|
|
if((!gl_isTrans(at, abx + x, aby + y)) &&
|
|
(!gl_isTrans(bt, bbx + x, bby + y))) {
|
|
|
|
/* Set the crash position. */
|
|
crash->x = x;
|
|
crash->y = y;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @fn int CollideLineLine(double s1x, double s1y, double e1x, double e1y,
|
|
* double s2x, double s2y, double e2x, double e2y) {
|
|
*
|
|
* @brief Check to see if two lines collide.
|
|
* @param[in] s1 .......
|
|
*/
|
|
int CollideLineLine(double s1x, double s1y, double e1x, double e1y,
|
|
double s2x, double s2y, double e2x, double e2y, Vec2* crash) {
|
|
|
|
double ua_t, ub_t, u_b;
|
|
double ua, ub;
|
|
|
|
ua_t = (e2x - s2x) * (s1y - s2y) - (e2y - s2y) * (s1x - s2x);
|
|
ub_t = (e1x - s1x) * (s1y - s2y) - (e1y - s1y) * (s1x - s2x);
|
|
u_b = (e2y - s2y) * (e1x - s1x) - (e2x - s2x) * (e1y - s1y);
|
|
|
|
if(u_b != 0.) {
|
|
ua = ua_t / u_b;
|
|
ub = ub_t / u_b;
|
|
|
|
/* Intersection at a point. */
|
|
if((0. <= ua) && (ua <= 1.) && (0. <= ub) && (ub <= 1.)) {
|
|
crash->x = s1x + ua * (e1x - s1x);
|
|
crash->y = s1y + ua * (e1y - s1y);
|
|
return 1;
|
|
} else
|
|
return 0;
|
|
} else {
|
|
/* Coincidence. */
|
|
if((ua_t == 0.) || (ub_t == 0.))
|
|
return 3;
|
|
/* Parallel. */
|
|
else
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn int CollideLineSprite(const Vec2* ap, double dir,
|
|
* const glTexture* bt, const int bsx, const int bsy,
|
|
* const Vec2& bp, Vec2* crash)
|
|
*
|
|
* @param[in] ap Origin of the line.
|
|
* @param[in] ad Direction of the line.
|
|
* @param[in] bt Texture b.
|
|
* @param[in] bsx Position of x of sprite b.
|
|
* @param[in] bsy Position of y of sprite b
|
|
* @param[in] bp Position in space of sprite b.
|
|
* @return 1 on Collision, else 0.
|
|
*
|
|
* @sa CollideSprite
|
|
*/
|
|
int CollideLineSprite(const Vec2* ap, double ad, double al,
|
|
const glTexture* bt, const int bsx, const int bsy, const Vec2* bp,
|
|
Vec2* crash) {
|
|
|
|
int x, y, rbsy, bbx, bby;
|
|
double ep[2], bl[2], tr[2], v[2], mod;
|
|
int hits, real_hits;
|
|
Vec2 tmp_crash, border[2];
|
|
|
|
/* Set up end point of line. */
|
|
ep[0] = ap->x + al*cos(ad);
|
|
ep[1] = ap->y + al*sin(ad);
|
|
|
|
/* Set up top right corner of the rectangle. */
|
|
tr[0] = bp->x + bt->sw/2.;
|
|
tr[1] = bp->y + bt->sh/2.;
|
|
|
|
/* Set up bottom left corner of the rectangle. */
|
|
bl[0] = bp->x - bt->sw/2.;
|
|
bl[1] = bp->y - bt->sh/2.;
|
|
|
|
/* Start check for rectangular collision. */
|
|
hits = 0;
|
|
|
|
/* Left border. */
|
|
if(CollideLineLine(ap->x, ap->y, ep[0], ep[1],
|
|
bl[0], bl[1], bl[0], tr[1], &tmp_crash) != 0) {
|
|
border[hits].x = tmp_crash.x;
|
|
border[hits].y = tmp_crash.y;
|
|
hits++;
|
|
}
|
|
/* Top border. */
|
|
if(CollideLineLine(ap->x, ap->y, ep[0], ep[1],
|
|
bl[0], tr[1], tr[0], tr[1], &tmp_crash) != 0) {
|
|
border[hits].x = tmp_crash.x;
|
|
border[hits].y = tmp_crash.y;
|
|
hits++;
|
|
}
|
|
/* Now we are going to have to make sure hits isn't 2. */
|
|
/* Right border. */
|
|
if((hits < 2) && CollideLineLine(ap->x, ap->y, ep[0], ep[1],
|
|
tr[0], tr[1], tr[0], bl[1], &tmp_crash) != 0) {
|
|
border[hits].x = tmp_crash.x;
|
|
border[hits].y = tmp_crash.y;
|
|
hits++;
|
|
}
|
|
/* Bottom border. */
|
|
if((hits < 2) && CollideLineLine(ap->x, ap->y, ep[0], ep[1],
|
|
tr[0], bl[1], bl[0], bl[1], &tmp_crash) != 0) {
|
|
border[hits].x = tmp_crash.x;
|
|
border[hits].y = tmp_crash.y;
|
|
hits++;
|
|
}
|
|
|
|
/* No hits - missed. */
|
|
if(hits == 2)
|
|
return 0;
|
|
|
|
/* Beam must die in the rectangle. */
|
|
if(hits == 1) {
|
|
border[1].x = ep[0];
|
|
border[1].y = ep[1];
|
|
}
|
|
|
|
/*
|
|
crash[0].x = border[0].x;
|
|
crash[0].y = border[0].y;
|
|
crash[1].x = border[1].x;
|
|
crash[1].y = border[1].y;
|
|
return 1;
|
|
*/
|
|
|
|
/* Now we do a pixel perfect approach. */
|
|
real_hits = 0;
|
|
/* directional vector (normalised). */
|
|
v[0] = border[1].x - border[0].x;
|
|
v[1] = border[1].y - border[0].y;
|
|
/* Normalize. */
|
|
mod = MOD(v[0], v[1])/2.; /* Multiply by two to reduce check amount. */
|
|
v[0] /= mod;
|
|
v[1] /= mod;
|
|
|
|
/* Real vertical sprite value (flipped). */
|
|
rbsy = bt->sy - bsy - 1;
|
|
/* Set up the base points. */
|
|
bbx = bsx*(int)(bt->sw);
|
|
bby = rbsy*(int)(bt->sh);
|
|
|
|
|
|
/* We start checking first border until we find collision. */
|
|
x = border[0].x - bl[0] + v[0];
|
|
y = border[0].y - bl[1] + v[1];
|
|
while((x > 0.) && (x < bt->sw) && (y > 0.) && (y < bt->sh)) {
|
|
/* Is non-transparent. */
|
|
if(!gl_isTrans(bt, bbx+(int)x, bby+(int)y)) {
|
|
crash[real_hits].x = x + bl[0];
|
|
crash[real_hits].y = y + bl[1];
|
|
real_hits++;
|
|
break;
|
|
}
|
|
x += v[0];
|
|
y += v[1];
|
|
}
|
|
|
|
/* Now we check the second border. */
|
|
x = border[1].x - bl[0] - v[0];
|
|
y = border[1].y - bl[1] - v[1];
|
|
while((x > 0.) && (x < bt->sw) && (y > 0.) && (y < bt->sh)) {
|
|
/* Is non-transparent. */
|
|
if(!gl_isTrans(bt, bbx+(int)x, bby+(int)y)) {
|
|
crash[real_hits].x = x + bl[0];
|
|
crash[real_hits].y = y + bl[1];
|
|
real_hits++;
|
|
break;
|
|
}
|
|
x -= v[0];
|
|
y -= v[1];
|
|
}
|
|
|
|
/* Actually missed. */
|
|
if(real_hits == 0)
|
|
return 0;
|
|
|
|
/* Strange situation, should never happen, but just in case we duplicate
|
|
* the hits. */
|
|
if(real_hits == 1) {
|
|
crash[1].x = crash[0].x;
|
|
crash[1].y = crash[0].y;
|
|
}
|
|
|
|
/* We hit! */
|
|
return 1;
|
|
}
|
|
|