naev 0.12.5
collision.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
9
11#include "naev.h"
12
13#include "SDL.h"
15
16#include "collision.h"
17
18#include "array.h"
19#include "log.h"
20#include "physics.h"
21
22/*
23 * Prototypes
24 */
25static int PointInPolygon( const CollPolyView *at, const vec2 *ap, float x,
26 float y );
27static int LineOnPolygon( const CollPolyView *at, const vec2 *ap, float x1,
28 float y1, float x2, float y2, vec2 *crash );
29
37void poly_load( CollPoly *polygon, xmlNodePtr base, const char *name )
38{
39 int n;
40 xmlr_attr_int_def( base, "num", n, 32 );
41 polygon->views = array_create_size( CollPolyView, n );
42
43 xmlNodePtr node = base->children;
44 do {
45 if ( !xml_isNode( node, "polygon" ) )
46 continue;
47
48 CollPolyView *view = &array_grow( &polygon->views );
49 view->x = array_create_size( float, 32 );
50 view->xmin = 0;
51 view->xmax = 0;
52 view->y = array_create_size( float, 32 );
53 view->ymin = 0;
54 view->ymax = 0;
55
56 xmlNodePtr cur = node->children;
57 do {
58 if ( xml_isNode( cur, "x" ) ) {
59 char *saveptr;
60 char *list = xml_get( cur );
61 /* split the list of coordinates */
62 char *ch = SDL_strtokr( list, ",", &saveptr );
63 while ( ch != NULL ) {
64 float d = atof( ch );
65 array_push_back( &view->x, d );
66 view->xmin = MIN( view->xmin, d );
67 view->xmax = MAX( view->xmax, d );
68 ch = SDL_strtokr( NULL, ",", &saveptr );
69 }
70 continue;
71 } else if ( xml_isNode( cur, "y" ) ) {
72 char *saveptr;
73 char *list = xml_get( cur );
74 /* split the list of coordinates */
75 char *ch = SDL_strtokr( list, ",", &saveptr );
76 while ( ch != NULL ) {
77 float d = atof( ch );
78 array_push_back( &view->y, d );
79 view->ymin = MIN( view->ymin, d );
80 view->ymax = MAX( view->ymax, d );
81 ch = SDL_strtokr( NULL, ",", &saveptr );
82 }
83 continue;
84 }
85 } while ( xml_nextNode( cur ) );
86
87 view->npt = array_size( view->x );
88 if ( array_size( view->y ) != view->npt )
89 WARN( _( "Polygon with mismatch of number of |x|=%d and |y|=%d "
90 "coordinates detected!" ),
91 view->npt, array_size( view->y ) );
92 } while ( xml_nextNode( node ) );
93
94 /* Compute useful offsets. */
95 polygon->dir_inc = 2. * M_PI / array_size( polygon->views );
96 polygon->dir_off = polygon->dir_inc * 0.5;
97
98 /* Some checks. */
99 double mean = 0.;
100 for ( int i = 0; i < array_size( polygon->views ); i++ ) {
101 CollPolyView *view = &polygon->views[i];
102 if ( view->npt < 3 )
103 WARN( _( "Polygon '%s' has under 3 points for view %d!" ), name, i );
104 mean += view->npt;
105 }
106 mean /= (double)array_size( polygon->views );
107 /* Second pass determines if they are too abnormal. */
108 for ( int i = 0; i < array_size( polygon->views ); i++ ) {
109 CollPolyView *view = &polygon->views[i];
110 if ( (double)view->npt < floor( mean * 0.4 ) )
111 WARN( _( "Polygon '%s' has very few points for view %d (%d points "
112 "compared to %f mean)!" ),
113 name, i, view->npt, mean );
114 // if ((double)view->npt > mean*3.)
115 // WARN(_("Polygon '%s' has too many points for view %d (%d points
116 // compared to %f mean)!"),name,i,view->npt, mean);
117 }
118}
119
120void poly_free( CollPoly *poly )
121{
122 for ( int i = 0; i < array_size( poly->views ); i++ ) {
123 CollPolyView *view = &poly->views[i];
124 array_free( view->x );
125 array_free( view->y );
126 }
127 array_free( poly->views );
128}
129
148int CollideSprite( const glTexture *at, const int asx, const int asy,
149 const vec2 *ap, const glTexture *bt, const int bsx,
150 const int bsy, const vec2 *bp, vec2 *crash )
151{
152 int x, y;
153 int ax1, ax2, ay1, ay2;
154 int bx1, bx2, by1, by2;
155 int inter_x0, inter_x1, inter_y0, inter_y1;
156 int rasy, rbsy;
157 int abx, aby, bbx, bby;
158
159#if DEBUGGING
160 /* Make sure the surfaces have transparency maps. */
161 if ( at->trans == NULL ) {
162 WARN( _( "Texture '%s' has no transparency map" ), at->name );
163 return 0;
164 }
165 if ( bt->trans == NULL ) {
166 WARN( _( "Texture '%s' has no transparency map" ), bt->name );
167 return 0;
168 }
169#endif /* DEBUGGING */
170
171 /* a - cube coordinates */
172 ax1 = (int)VX( *ap ) - (int)( at->sw ) / 2;
173 ay1 = (int)VY( *ap ) - (int)( at->sh ) / 2;
174 ax2 = ax1 + (int)( at->sw ) - 1;
175 ay2 = ay1 + (int)( at->sh ) - 1;
176
177 /* b - cube coordinates */
178 bx1 = (int)VX( *bp ) - (int)( bt->sw ) / 2;
179 by1 = (int)VY( *bp ) - (int)( bt->sh ) / 2;
180 bx2 = bx1 + bt->sw - 1;
181 by2 = by1 + bt->sh - 1;
182
183 /* check if bounding boxes intersect */
184 if ( ( bx2 < ax1 ) || ( ax2 < bx1 ) )
185 return 0;
186 if ( ( by2 < ay1 ) || ( ay2 < by1 ) )
187 return 0;
188
189 /* define the remaining binding box */
190 inter_x0 = MAX( ax1, bx1 );
191 inter_x1 = MIN( ax2, bx2 );
192 inter_y0 = MAX( ay1, by1 );
193 inter_y1 = MIN( ay2, by2 );
194
195 /* real vertical sprite value (flipped) */
196 rasy = at->sy - asy - 1;
197 rbsy = bt->sy - bsy - 1;
198
199 /* set up the base points */
200 abx = asx * (int)( at->sw ) - ax1;
201 aby = rasy * (int)( at->sh ) - ay1;
202 bbx = bsx * (int)( bt->sw ) - bx1;
203 bby = rbsy * (int)( bt->sh ) - by1;
204
205 for ( y = inter_y0; y <= inter_y1; y++ )
206 for ( x = inter_x0; x <= inter_x1; x++ )
207 /* compute offsets for surface before pass to TransparentPixel test */
208 if ( ( !gl_isTrans( at, abx + x, aby + y ) ) &&
209 ( !gl_isTrans( bt, bbx + x, bby + y ) ) ) {
210
211 /* Set the crash position. */
212 crash->x = x;
213 crash->y = y;
214 return 1;
215 }
216
217 return 0;
218}
219
233int CollideSpritePolygon( const CollPolyView *at, const vec2 *ap,
234 const glTexture *bt, int bsx, int bsy, const vec2 *bp,
235 vec2 *crash )
236{
237 int x, y;
238 int ax1, ax2, ay1, ay2;
239 int bx1, bx2, by1, by2;
240 int inter_x0, inter_x1, inter_y0, inter_y1;
241 int rbsy;
242 int bbx, bby;
243
244#if DEBUGGING
245 /* Make sure the surfaces have transparency maps. */
246 if ( bt->trans == NULL ) {
247 WARN( _( "Texture '%s' has no transparency map" ), bt->name );
248 return 0;
249 }
250#endif /* DEBUGGING */
251
252 /* a - cube coordinates */
253 ax1 = (int)VX( *ap ) + (int)( at->xmin );
254 ay1 = (int)VY( *ap ) + (int)( at->ymin );
255 ax2 = (int)VX( *ap ) + (int)( at->xmax );
256 ay2 = (int)VY( *ap ) + (int)( at->ymax );
257
258 /* b - cube coordinates */
259 bx1 = (int)VX( *bp ) - (int)( bt->sw ) / 2;
260 by1 = (int)VY( *bp ) - (int)( bt->sh ) / 2;
261 bx2 = bx1 + bt->sw - 1;
262 by2 = by1 + bt->sh - 1;
263
264 /* check if bounding boxes intersect */
265 if ( ( bx2 < ax1 ) || ( ax2 < bx1 ) )
266 return 0;
267 if ( ( by2 < ay1 ) || ( ay2 < by1 ) )
268 return 0;
269
270 /* define the remaining binding box */
271 inter_x0 = MAX( ax1, bx1 );
272 inter_x1 = MIN( ax2, bx2 );
273 inter_y0 = MAX( ay1, by1 );
274 inter_y1 = MIN( ay2, by2 );
275
276 /* real vertical sprite value (flipped) */
277 rbsy = bt->sy - bsy - 1;
278
279 /* set up the base points */
280 bbx = bsx * (int)( bt->sw ) - bx1;
281 bby = rbsy * (int)( bt->sh ) - by1;
282 for ( y = inter_y0; y <= inter_y1; y++ ) {
283 for ( x = inter_x0; x <= inter_x1; x++ ) {
284 /* compute offsets for surface before pass to TransparentPixel test */
285 if ( ( !gl_isTrans( bt, bbx + x, bby + y ) ) ) {
286 if ( PointInPolygon( at, ap, (float)x, (float)y ) ) {
287 crash->x = x;
288 crash->y = y;
289 return 1;
290 }
291 }
292 }
293 }
294
295 return 0;
296}
297
311int CollidePolygon( const CollPolyView *at, const vec2 *ap,
312 const CollPolyView *bt, const vec2 *bp, vec2 *crash )
313{
314 int ax1, ax2, ay1, ay2;
315 int bx1, bx2, by1, by2;
316 int inter_x0, inter_x1, inter_y0, inter_y1;
317 float x1, y1, x2, y2;
318
319 /* a - cube coordinates */
320 ax1 = (int)VX( *ap ) + (int)( at->xmin );
321 ay1 = (int)VY( *ap ) + (int)( at->ymin );
322 ax2 = (int)VX( *ap ) + (int)( at->xmax );
323 ay2 = (int)VY( *ap ) + (int)( at->ymax );
324
325 /* b - cube coordinates */
326 bx1 = (int)VX( *bp ) + (int)( bt->xmin );
327 by1 = (int)VY( *bp ) + (int)( bt->ymin );
328 bx2 = (int)VX( *bp ) + (int)( bt->xmax );
329 by2 = (int)VY( *bp ) + (int)( bt->ymax );
330
331 /* check if bounding boxes intersect */
332 if ( ( bx2 < ax1 ) || ( ax2 < bx1 ) )
333 return 0;
334 if ( ( by2 < ay1 ) || ( ay2 < by1 ) )
335 return 0;
336
337 /* define the remaining binding box */
338 inter_x0 = MAX( ax1, bx1 );
339 inter_x1 = MIN( ax2, bx2 );
340 inter_y0 = MAX( ay1, by1 );
341 inter_y1 = MIN( ay2, by2 );
342
343 /* loop on the points of bt to see if one of them is in polygon at. */
344 for ( int i = 0; i <= bt->npt - 1; i++ ) {
345 float xabs, yabs;
346 xabs = bt->x[i] + VX( *bp );
347 yabs = bt->y[i] + VY( *bp );
348
349 if ( ( xabs < inter_x0 ) || ( xabs > inter_x1 ) || ( yabs < inter_y0 ) ||
350 ( yabs > inter_y1 ) ) {
351 if ( PointInPolygon( at, ap, xabs, yabs ) ) {
352 crash->x = (int)xabs;
353 crash->y = (int)yabs;
354 return 1;
355 }
356 }
357 }
358
359 /* loop on the lines of bt to see if one of them intersects a line of at. */
360 x1 = bt->x[0] + VX( *bp );
361 y1 = bt->y[0] + VY( *bp );
362 x2 = bt->x[bt->npt - 1] + VX( *bp );
363 y2 = bt->y[bt->npt - 1] + VY( *bp );
364 if ( LineOnPolygon( at, ap, x1, y1, x2, y2, crash ) )
365 return 1;
366 for ( int i = 0; i <= bt->npt - 2; i++ ) {
367 x1 = bt->x[i] + VX( *bp );
368 y1 = bt->y[i] + VY( *bp );
369 x2 = bt->x[i + 1] + VX( *bp );
370 y2 = bt->y[i + 1] + VY( *bp );
371 if ( LineOnPolygon( at, ap, x1, y1, x2, y2, crash ) )
372 return 1;
373 }
374
375 return 0;
376}
377
385void poly_rotate( CollPolyView *rpolygon, const CollPolyView *ipolygon,
386 float theta )
387{
388 float ct, st;
389
390 rpolygon->npt = ipolygon->npt;
391 rpolygon->x = malloc( ipolygon->npt * sizeof( float ) );
392 rpolygon->y = malloc( ipolygon->npt * sizeof( float ) );
393 rpolygon->xmin = 0;
394 rpolygon->xmax = 0;
395 rpolygon->ymin = 0;
396 rpolygon->ymax = 0;
397
398 ct = cos( theta );
399 st = sin( theta );
400
401 for ( int i = 0; i <= rpolygon->npt - 1; i++ ) {
402 float d = ipolygon->x[i] * ct - ipolygon->y[i] * st;
403 rpolygon->x[i] = d;
404 rpolygon->xmin = MIN( rpolygon->xmin, d );
405 rpolygon->xmax = MAX( rpolygon->xmax, d );
406
407 d = ipolygon->x[i] * st + ipolygon->y[i] * ct;
408 rpolygon->y[i] = d;
409 rpolygon->ymin = MIN( rpolygon->ymin, d );
410 rpolygon->ymax = MAX( rpolygon->ymax, d );
411 }
412}
413
414const CollPolyView *poly_view( const CollPoly *poly, double dir )
415{
416 if ( array_size( poly->views ) <= 0 )
417 return NULL;
418
419#ifdef DEBUGGING
420 if ( ( dir > 2. * M_PI ) || ( dir < 0. ) ) {
421 WARN( _( "Angle not between 0 and 2.*M_PI [%f]." ), dir );
422 dir = angle_clean( dir );
423 }
424#endif /* DEBUGGING */
425
426 int s = ( dir + poly->dir_off ) / poly->dir_inc;
427 s = s % array_size( poly->views );
428 return &poly->views[s];
429}
430
440static int PointInPolygon( const CollPolyView *at, const vec2 *ap, float x,
441 float y )
442{
443 float vprod, sprod, angle;
444 float dxi, dxip, dyi, dyip;
445
446 /* See if the pixel is inside the polygon:
447 We increment the angle when doing a loop along all the points
448 If the final angle is 0, we are outside the polygon
449 Otherwise, the angle should be +/- 2pi*/
450 angle = 0.0;
451 for ( int i = 0; i <= at->npt - 2; i++ ) {
452 dxi = at->x[i] + VX( *ap ) - x;
453 dxip = at->x[i + 1] + VX( *ap ) - x;
454 dyi = at->y[i] + VY( *ap ) - y;
455 dyip = at->y[i + 1] + VY( *ap ) - y;
456 sprod = dxi * dxip + dyi * dyip;
457 vprod = dxi * dyip - dyi * dxip;
458 angle += atan2( vprod, sprod );
459 }
460 dxi = at->x[at->npt - 1] + VX( *ap ) - x;
461 dxip = at->x[0] + VX( *ap ) - x;
462 dyi = at->y[at->npt - 1] + VY( *ap ) - y;
463 dyip = at->y[0] + VY( *ap ) - y;
464 sprod = dxi * dxip + dyi * dyip;
465 vprod = dxi * dyip - dyi * dxip;
466 angle += atan2( vprod, sprod );
467
468 if ( FABS( angle ) < DOUBLE_TOL )
469 return 0;
470
471 return 1;
472}
473
486static int LineOnPolygon( const CollPolyView *at, const vec2 *ap, float x1,
487 float y1, float x2, float y2, vec2 *crash )
488{
489 float xi, xip, yi, yip;
490
491 /* In this function, we are only looking for one collision point. */
492
493 xi = at->x[at->npt - 1] + ap->x;
494 xip = at->x[0] + ap->x;
495 yi = at->y[at->npt - 1] + ap->y;
496 yip = at->y[0] + ap->y;
497 if ( CollideLineLine( x1, y1, x2, y2, xi, yi, xip, yip, crash ) == 1 )
498 return 1;
499 for ( int i = 0; i <= at->npt - 2; i++ ) {
500 xi = at->x[i] + ap->x;
501 xip = at->x[i + 1] + ap->x;
502 yi = at->y[i] + ap->y;
503 yip = at->y[i + 1] + ap->y;
504 if ( CollideLineLine( x1, y1, x2, y2, xi, yi, xip, yip, crash ) == 1 )
505 return 1;
506 }
507
508 return 0;
509}
510
526int CollideLineLine( double s1x, double s1y, double e1x, double e1y, double s2x,
527 double s2y, double e2x, double e2y, vec2 *crash )
528{
529 double ua_t, ub_t, u_b;
530
531 ua_t = ( e2x - s2x ) * ( s1y - s2y ) - ( e2y - s2y ) * ( s1x - s2x );
532 ub_t = ( e1x - s1x ) * ( s1y - s2y ) - ( e1y - s1y ) * ( s1x - s2x );
533 u_b = ( e2y - s2y ) * ( e1x - s1x ) - ( e2x - s2x ) * ( e1y - s1y );
534
535 if ( u_b != 0. ) {
536 double ua, ub;
537 ua = ua_t / u_b;
538 ub = ub_t / u_b;
539
540 /* Intersection at a point. */
541 if ( ( 0. <= ua ) && ( ua <= 1. ) && ( 0. <= ub ) && ( ub <= 1. ) ) {
542 crash->x = s1x + ua * ( e1x - s1x );
543 crash->y = s1y + ua * ( e1y - s1y );
544 return 1;
545 }
546 /* No intersection. */
547 else
548 return 0;
549 } else {
550 /* Coincident. */
551 if ( ( ua_t == 0. ) || ( ub_t == 0. ) )
552 return 3;
553 /* Parallel. */
554 else
555 return 2;
556 }
557}
558
578int CollideLineSprite( const vec2 *ap, double ad, double al,
579 const glTexture *bt, const int bsx, const int bsy,
580 const vec2 *bp, vec2 crash[2] )
581{
582 int x, y, rbsy, bbx, bby;
583 double ep[2], bl[2], tr[2], v[2], mod;
584 int hits, real_hits;
585 vec2 tmp_crash, border[2];
586
587 /* Make sure texture has transparency map. */
588 if ( bt->trans == NULL ) {
589 WARN( _( "Texture '%s' has no transparency map" ), bt->name );
590 return 0;
591 }
592
593 /* Set up end point of line. */
594 ep[0] = ap->x + al * cos( ad );
595 ep[1] = ap->y + al * sin( ad );
596
597 /* Set up top right corner of the rectangle. */
598 tr[0] = bp->x + bt->sw / 2.;
599 tr[1] = bp->y + bt->sh / 2.;
600 /* Set up bottom left corner of the rectangle. */
601 bl[0] = bp->x - bt->sw / 2.;
602 bl[1] = bp->y - bt->sh / 2.;
603
604 /*
605 * Start check for rectangular collisions.
606 */
607 hits = 0;
608 /* Left border. */
609 if ( CollideLineLine( ap->x, ap->y, ep[0], ep[1], bl[0], bl[1], bl[0], tr[1],
610 &tmp_crash ) == 1 ) {
611 border[hits].x = tmp_crash.x;
612 border[hits].y = tmp_crash.y;
613 hits++;
614 }
615 /* Top border. */
616 if ( CollideLineLine( ap->x, ap->y, ep[0], ep[1], bl[0], tr[1], tr[0], tr[1],
617 &tmp_crash ) == 1 ) {
618 border[hits].x = tmp_crash.x;
619 border[hits].y = tmp_crash.y;
620 hits++;
621 }
622 /* Now we have to make sure hits isn't 2. */
623 /* Right border. */
624 if ( ( hits < 2 ) &&
625 CollideLineLine( ap->x, ap->y, ep[0], ep[1], tr[0], tr[1], tr[0], bl[1],
626 &tmp_crash ) == 1 ) {
627 border[hits].x = tmp_crash.x;
628 border[hits].y = tmp_crash.y;
629 hits++;
630 }
631 /* Bottom border. */
632 if ( ( hits < 2 ) &&
633 CollideLineLine( ap->x, ap->y, ep[0], ep[1], tr[0], bl[1], bl[0], bl[1],
634 &tmp_crash ) == 1 ) {
635 border[hits].x = tmp_crash.x;
636 border[hits].y = tmp_crash.y;
637 hits++;
638 }
639
640 /* No hits - missed. */
641 if ( hits == 0 )
642 return 0;
643
644 /* Beam must die in the rectangle. */
645 if ( hits == 1 ) {
646 border[1].x = ep[0];
647 border[1].y = ep[1];
648 }
649
650 /*
651 * Now we do a pixel perfect approach.
652 */
653 real_hits = 0;
654 /* Directionality vector (normalized). */
655 v[0] = border[1].x - border[0].x;
656 v[1] = border[1].y - border[0].y;
657 /* Normalize. */
658 mod = MOD( v[0], v[1] ) / 2.; /* Multiply by two to reduce check amount. */
659 v[0] /= mod;
660 v[1] /= mod;
661
662 /* real vertical sprite value (flipped) */
663 rbsy = bt->sy - bsy - 1;
664 /* set up the base points */
665 bbx = bsx * (int)( bt->sw );
666 bby = rbsy * (int)( bt->sh );
667
668 /* We start checking first border until we find collision. */
669 x = border[0].x - bl[0] + v[0];
670 y = border[0].y - bl[1] + v[1];
671 while ( ( x > 0. ) && ( x < bt->sw ) && ( y > 0. ) && ( y < bt->sh ) ) {
672 /* Is non-transparent. */
673 if ( !gl_isTrans( bt, bbx + (int)x, bby + (int)y ) ) {
674 crash[real_hits].x = x + bl[0];
675 crash[real_hits].y = y + bl[1];
676 real_hits++;
677 break;
678 }
679 x += v[0];
680 y += v[1];
681 }
682
683 /* Now we check the second border. */
684 x = border[1].x - bl[0] - v[0];
685 y = border[1].y - bl[1] - v[1];
686 while ( ( x > 0. ) && ( x < bt->sw ) && ( y > 0. ) && ( y < bt->sh ) ) {
687 /* Is non-transparent. */
688 if ( !gl_isTrans( bt, bbx + (int)x, bby + (int)y ) ) {
689 crash[real_hits].x = x + bl[0];
690 crash[real_hits].y = y + bl[1];
691 real_hits++;
692 break;
693 }
694 x -= v[0];
695 y -= v[1];
696 }
697
698 /* Actually missed. */
699 if ( real_hits == 0 )
700 return 0;
701
702 /* Strange situation, should never happen but just in case we duplicate
703 * the hit. */
704 if ( real_hits == 1 ) {
705 crash[1].x = crash[0].x;
706 crash[1].y = crash[0].y;
707 }
708
709 /* We hit. */
710 return 1;
711}
712
729int CollideLinePolygon( const vec2 *ap, double ad, double al,
730 const CollPolyView *bt, const vec2 *bp, vec2 crash[2] )
731{
732 double ep[2];
733 double xi, yi, xip, yip;
734 int real_hits;
735 vec2 tmp_crash;
736
737 /* Set up end point of line. */
738 ep[0] = ap->x + al * cos( ad );
739 ep[1] = ap->y + al * sin( ad );
740
741 real_hits = 0;
742 vectnull( &tmp_crash );
743
744 /* Check if the beginning point is inside polygon */
745 if ( PointInPolygon( bt, bp, (float)ap->x, (float)ap->y ) ) {
746 crash[real_hits].x = ap->x;
747 crash[real_hits].y = ap->y;
748 real_hits++;
749 }
750
751 /* same thing for end point */
752 if ( PointInPolygon( bt, bp, (float)ep[0], (float)ep[1] ) ) {
753 crash[real_hits].x = ep[0];
754 crash[real_hits].y = ep[1];
755 real_hits++;
756 }
757
758 /* If both are inside, we got the two collision points. */
759 if ( real_hits == 2 )
760 return 1;
761
762 /* None is inside, check if there is a chance of intersection */
763 if ( real_hits == 0 ) {
764 int hits;
765 double bl[2], tr[2];
766 /* Set up top right corner of the rectangle. */
767 tr[0] = bp->x + (double)bt->xmax;
768 tr[1] = bp->y + (double)bt->ymax;
769 /* Set up bottom left corner of the rectangle. */
770 bl[0] = bp->x + (double)bt->xmin;
771 bl[1] = bp->y + (double)bt->ymin;
772
773 /*
774 * Start check for rectangular collisions.
775 */
776 hits = 0;
777 /* Left border. */
778 if ( CollideLineLine( ap->x, ap->y, ep[0], ep[1], bl[0], bl[1], bl[0],
779 tr[1], &tmp_crash ) == 1 )
780 hits++;
781
782 /* Top border. */
783 if ( !hits && CollideLineLine( ap->x, ap->y, ep[0], ep[1], bl[0], tr[1],
784 tr[0], tr[1], &tmp_crash ) == 1 )
785 hits++;
786
787 /* Right border. */
788 if ( !hits && CollideLineLine( ap->x, ap->y, ep[0], ep[1], tr[0], tr[1],
789 tr[0], bl[1], &tmp_crash ) == 1 )
790 hits++;
791
792 /* Bottom border. */
793 if ( !hits && CollideLineLine( ap->x, ap->y, ep[0], ep[1], tr[0], bl[1],
794 bl[0], bl[1], &tmp_crash ) == 1 )
795 hits++;
796
797 /* No hits - missed. No need to go further */
798 if ( hits == 0 )
799 return 0;
800 }
801
802 /*
803 * Now we check any line of the polygon
804 */
805 xi = (double)bt->x[bt->npt - 1] + bp->x;
806 xip = (double)bt->x[0] + bp->x;
807 yi = (double)bt->y[bt->npt - 1] + bp->y;
808 yip = (double)bt->y[0] + bp->y;
809 if ( CollideLineLine( ap->x, ap->y, ep[0], ep[1], xi, yi, xip, yip,
810 &tmp_crash ) ) {
811 crash[real_hits].x = tmp_crash.x;
812 crash[real_hits].y = tmp_crash.y;
813 real_hits++;
814 if ( real_hits == 2 )
815 return 1;
816 }
817 for ( int i = 0; i <= bt->npt - 2; i++ ) {
818 xi = (double)bt->x[i] + bp->x;
819 xip = (double)bt->x[i + 1] + bp->x;
820 yi = (double)bt->y[i] + bp->y;
821 yip = (double)bt->y[i + 1] + bp->y;
822 if ( CollideLineLine( ap->x, ap->y, ep[0], ep[1], xi, yi, xip, yip,
823 &tmp_crash ) ) {
824 crash[real_hits].x = tmp_crash.x;
825 crash[real_hits].y = tmp_crash.y;
826 real_hits++;
827 if ( real_hits == 2 )
828 return 1;
829 }
830 }
831
832 /* Actually missed. */
833 if ( real_hits == 0 )
834 return 0;
835
836 /* Strange situation, should never happen but just in case we duplicate
837 * the hit. */
838 if ( real_hits == 1 ) {
839 crash[1].x = crash[0].x;
840 crash[1].y = crash[0].y;
841 }
842
843 /* We hit. */
844 return 1;
845}
846
862int CollideCirclePolygon( const vec2 *ap, double ar, const CollPolyView *bt,
863 const vec2 *bp, vec2 crash[2] )
864{
865 vec2 p1, p2;
866 int real_hits;
867 vec2 tmp_crash[2];
868
869 real_hits = 0;
870 vectnull( &tmp_crash[0] );
871 vectnull( &tmp_crash[1] );
872
873 /* Set up top right corner of the rectangle. */
874 p1.x = bp->x + (double)bt->xmax;
875 p1.y = bp->y + (double)bt->ymax;
876 /* Set up bottom left corner of the rectangle. */
877 p2.x = bp->x + (double)bt->xmin;
878 p2.y = bp->y + (double)bt->ymin;
879
880 /* Start check for rectangular collisions. */
881 if ( ( ap->x - ar > p1.x ) && ( ap->x + ar < p2.x ) &&
882 ( ap->y - ar > p1.y ) && ( ap->y + ar < p2.y ) )
883 return 0;
884
885 /*
886 * Now we check any line of the polygon
887 */
888 p1.x = (double)bt->x[bt->npt - 1] + bp->x;
889 p2.x = (double)bt->x[0] + bp->x;
890 p1.y = (double)bt->y[bt->npt - 1] + bp->y;
891 p2.y = (double)bt->y[0] + bp->y;
892 if ( CollideLineCircle( &p1, &p2, ap, ar, tmp_crash ) ) {
893 crash[real_hits].x = tmp_crash[0].x;
894 crash[real_hits].y = tmp_crash[0].y;
895 real_hits++;
896 if ( real_hits == 2 )
897 return 1;
898 }
899 for ( int i = 0; i <= bt->npt - 2; i++ ) {
900 p1.x = (double)bt->x[i] + bp->x;
901 p2.x = (double)bt->x[i + 1] + bp->x;
902 p1.y = (double)bt->y[i] + bp->y;
903 p2.y = (double)bt->y[i + 1] + bp->y;
904 if ( CollideLineCircle( &p1, &p2, ap, ar, tmp_crash ) ) {
905 crash[real_hits].x = tmp_crash[0].x;
906 crash[real_hits].y = tmp_crash[0].y;
907 real_hits++;
908 if ( real_hits == 2 )
909 return 1;
910 }
911 }
912
913 /* Actually missed. */
914 if ( real_hits == 0 )
915 return 0;
916
917 /* Strange situation, should never happen but just in case we duplicate
918 * the hit. */
919 if ( real_hits == 1 ) {
920 crash[1].x = crash[0].x;
921 crash[1].y = crash[0].y;
922 }
923
924 /* We hit. */
925 return 1;
926}
927
941int CollideCircleSprite( const vec2 *ap, double ar, const glTexture *bt,
942 const int bsx, const int bsy, const vec2 *bp,
943 vec2 *crash )
944{
945 int r, acx, acy, ax1, ax2, ay1, ay2;
946 int bx1, bx2, by1, by2;
947 int inter_x0, inter_x1, inter_y0, inter_y1;
948 int rbsy;
949 int bbx, bby;
950
951#if DEBUGGING
952 /* Make sure the surfaces have transparency maps. */
953 if ( bt->trans == NULL ) {
954 WARN( _( "Texture '%s' has no transparency map" ), bt->name );
955 return 0;
956 }
957#endif /* DEBUGGING */
958
959 /* a - cube coordinates */
960 r = ceil( ar );
961 acx = (int)VX( *ap );
962 acy = (int)VY( *ap );
963 ax1 = acx - r;
964 ay1 = acy - r;
965 ax2 = acx + r;
966 ay2 = acy + r;
967
968 /* b - cube coordinates */
969 bx1 = (int)VX( *bp ) - (int)( bt->sw ) / 2;
970 by1 = (int)VY( *bp ) - (int)( bt->sh ) / 2;
971 bx2 = bx1 + bt->sw - 1;
972 by2 = by1 + bt->sh - 1;
973
974 /* check if bounding boxes intersect */
975 if ( ( bx2 < ax1 ) || ( ax2 < bx1 ) )
976 return 0;
977 if ( ( by2 < ay1 ) || ( ay2 < by1 ) )
978 return 0;
979
980 /* define the remaining binding box */
981 inter_x0 = MAX( ax1, bx1 );
982 inter_x1 = MIN( ax2, bx2 );
983 inter_y0 = MAX( ay1, by1 );
984 inter_y1 = MIN( ay2, by2 );
985
986 /* real vertical sprite value (flipped) */
987 rbsy = bt->sy - bsy - 1;
988
989 /* set up the base points */
990 bbx = bsx * (int)( bt->sw ) - bx1;
991 bby = rbsy * (int)( bt->sh ) - by1;
992 for ( int y = inter_y0; y <= inter_y1; y++ ) {
993 for ( int x = inter_x0; x <= inter_x1; x++ ) {
994 /* compute offsets for surface before pass to TransparentPixel test */
995 if ( ( !gl_isTrans( bt, bbx + x, bby + y ) ) ) {
996 if ( pow2( x - acx ) + pow2( y - acy ) <= r * r ) {
997 crash->x = x;
998 crash->y = y;
999 return 1;
1000 }
1001 }
1002 }
1003 }
1004
1005 return 0;
1006}
1007
1008static int linePointOnSegment( double d1, double x1, double y1, double x2,
1009 double y2, double x, double y )
1010{
1011 // double d1 = hypot( x2-x1, y2-y1 ); /* Distance between end-points. */
1012 double d2 = hypot( x - x1, y - y1 ); /* Distance from point to one end. */
1013 double d3 = hypot( x2 - x, y2 - y ); /* Distance to the other end. */
1014 return fabs( d1 - d2 - d3 ) <
1015 DOUBLE_TOL; /* True if smaller than some tolerance. */
1016}
1017
1018#define FX( A, B, C, x ) ( -( A * x + C ) / B )
1019#define FY( A, B, C, y ) ( -( B * y + C ) / A )
1030int CollideLineCircle( const vec2 *p1, const vec2 *p2, const vec2 *cc,
1031 double cr, vec2 crash[2] )
1032{
1033 double x0 = cc->x;
1034 double y0 = cc->y;
1035 double x1 = p1->x;
1036 double y1 = p1->y;
1037 double x2 = p2->x;
1038 double y2 = p2->y;
1039
1040 /* Case line in circle. */
1041 double r2 = cr * cr;
1042 if ( ( vec2_dist2( p1, cc ) < r2 ) && ( vec2_dist2( p2, cc ) < r2 ) ) {
1043 crash[0].x = ( x1 + x2 ) * 0.5;
1044 crash[0].y = ( y1 + y2 ) * 0.5;
1045 return 1;
1046 }
1047
1048 double A = y2 - y1;
1049 double B = x1 - x2;
1050 double C = x2 * y1 - x1 * y2;
1051
1052 double a = pow2( A ) + pow2( B );
1053 double b, c, d;
1054
1055 int bnz, cnt;
1056
1057 double x, y, d1;
1058
1059 /* Non-vertical case. */
1060 if ( fabs( B ) >= 1e-8 ) {
1061 b = 2. * ( A * C + A * B * y0 - pow2( B ) * x0 );
1062 c = pow2( C ) + 2. * B * C * y0 -
1063 pow2( B ) * ( pow2( cr ) - pow2( x0 ) - pow2( y0 ) );
1064 bnz = 1;
1065 }
1066 /* Have to have special care when line is vertical. */
1067 else {
1068 b = 2. * ( B * C + A * B * x0 - pow2( A ) * y0 );
1069 c = pow2( C ) + 2. * A * C * x0 -
1070 pow2( A ) * ( pow2( cr ) - pow2( x0 ) - pow2( y0 ) );
1071 bnz = 0;
1072 }
1073 d = pow2( b ) - 4. * a * c; /* Discriminant. */
1074 if ( d < 0. )
1075 return 0;
1076
1077 cnt = 0;
1078 d1 = hypot( x2 - x1, y2 - y1 );
1079 /* Line is tangent, so only one intersection. */
1080 if ( d == 0. ) {
1081 if ( bnz ) {
1082 x = -b / ( 2. * a );
1083 y = FX( A, B, C, x );
1084 if ( linePointOnSegment( d1, x1, y1, x2, y2, x, y ) ) {
1085 crash[cnt].x = x;
1086 crash[cnt].y = y;
1087 cnt++;
1088 }
1089 } else {
1090 y = -b / ( 2. * a );
1091 x = FY( A, B, C, y );
1092 if ( linePointOnSegment( d1, x1, y1, x2, y2, x, y ) ) {
1093 crash[cnt].x = x;
1094 crash[cnt].y = y;
1095 cnt++;
1096 }
1097 }
1098 }
1099 /* Two intersections. */
1100 else {
1101 d = sqrt( d );
1102 if ( bnz ) {
1103 x = ( -b + d ) / ( 2. * a );
1104 y = FX( A, B, C, x );
1105 if ( linePointOnSegment( d1, x1, y1, x2, y2, x, y ) ) {
1106 crash[cnt].x = x;
1107 crash[cnt].y = y;
1108 cnt++;
1109 }
1110 x = ( -b - d ) / ( 2. * a );
1111 y = FX( A, B, C, x );
1112 if ( linePointOnSegment( d1, x1, y1, x2, y2, x, y ) ) {
1113 crash[cnt].x = x;
1114 crash[cnt].y = y;
1115 cnt++;
1116 }
1117 } else {
1118 y = ( -b + d ) / ( 2. * a );
1119 x = FY( A, B, C, y );
1120 if ( linePointOnSegment( d1, x1, y1, x2, y2, x, y ) ) {
1121 crash[cnt].x = x;
1122 crash[cnt].y = y;
1123 cnt++;
1124 }
1125 y = ( -b - d ) / ( 2. * a );
1126 x = FY( A, B, C, y );
1127 if ( linePointOnSegment( d1, x1, y1, x2, y2, x, y ) ) {
1128 crash[cnt].x = x;
1129 crash[cnt].y = y;
1130 cnt++;
1131 }
1132 }
1133 }
1134 return cnt;
1135}
1136#undef FX
1137#undef FY
1138
1142int CollideCircleCircle( const vec2 *p1, double r1, const vec2 *p2, double r2,
1143 vec2 crash[2] )
1144{
1145 double dist2 = vec2_dist2( p1, p2 );
1146
1147 /* No intersection. */
1148 if ( dist2 > pow2( r1 + r2 ) )
1149 return 0;
1150
1151 crash->x = ( p1->x * r1 + p2->x * r2 ) / ( r1 + r2 );
1152 crash->y = ( p1->y * r1 + p2->y * r2 ) / ( r1 + r2 );
1153 return 1;
1154}
1155
1159double CollideCircleIntersection( const vec2 *p1, double r1, const vec2 *p2,
1160 double r2 )
1161{
1162 double dist2 = vec2_dist2( p1, p2 );
1163
1164 /* No intersection. */
1165 if ( dist2 > pow2( r1 + r2 ) )
1166 return 0.;
1167
1168 /* One inside the other. */
1169 if ( dist2 <= pow2( fabs( r1 - r2 ) ) )
1170 return M_PI * pow2( MIN( r1, r2 ) );
1171
1172#if 0
1173 /* Case exactly the same. */
1174 if ((dist2==0.) && (r1==r2))
1175 return M_PI * pow2(r1);
1176#endif
1177
1178 /* Distances. */
1179 double dist = sqrt( dist2 );
1180 double distc1 = ( pow2( r1 ) - pow2( r2 ) + dist2 ) /
1181 ( 2. * dist ); /* First center point to middle line. */
1182 double distc2 = dist - distc1; /* Second center point to middle line. */
1183 double height =
1184 sqrt( pow2( r1 ) - pow2( distc1 ) ); /* Half of middle line. */
1185 /* Angles. */
1186 double ang1 = fmod( atan2( height, distc1 ) * 2. + 2. * M_PI,
1187 2. * M_PI ); /* Center angle for first circle. */
1188 double ang2 = fmod( atan2( height, distc2 ) * 2. + 2. * M_PI,
1189 2. * M_PI ); /*< Center angle for second circle. */
1190 /* Areas. */
1191 double A1 = pow2( r1 ) / 2.0 *
1192 ( ang1 - sin( ang1 ) ); /* Area of first circula segment. */
1193 double A2 = pow2( r2 ) / 2.0 *
1194 ( ang2 - sin( ang2 ) ); /* Area of second circula segment. */
1195
1196 return A1 + A2;
1197}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
#define array_create_size(basic_type, capacity)
Creates a new dynamic array of ‘basic_type’ with an initial capacity.
Definition array.h:102
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:122
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
int CollideCircleCircle(const vec2 *p1, double r1, const vec2 *p2, double r2, vec2 crash[2])
Computes the collision between two circles.
Definition collision.c:1142
int CollideSpritePolygon(const CollPolyView *at, const vec2 *ap, const glTexture *bt, int bsx, int bsy, const vec2 *bp, vec2 *crash)
Checks whether or not a sprite collides with a polygon.
Definition collision.c:233
static int PointInPolygon(const CollPolyView *at, const vec2 *ap, float x, float y)
Checks whether or not a point is inside a polygon.
Definition collision.c:440
void poly_rotate(CollPolyView *rpolygon, const CollPolyView *ipolygon, float theta)
Rotates a polygon.
Definition collision.c:385
int CollideLineCircle(const vec2 *p1, const vec2 *p2, const vec2 *cc, double cr, vec2 crash[2])
Checks to see if a line collides with a circle.
Definition collision.c:1030
int CollideLineSprite(const vec2 *ap, double ad, double al, const glTexture *bt, const int bsx, const int bsy, const vec2 *bp, vec2 crash[2])
Checks to see if a line collides with a sprite.
Definition collision.c:578
int CollideCircleSprite(const vec2 *ap, double ar, const glTexture *bt, const int bsx, const int bsy, const vec2 *bp, vec2 *crash)
Checks whether or not a sprite collides with a polygon.
Definition collision.c:941
int CollideCirclePolygon(const vec2 *ap, double ar, const CollPolyView *bt, const vec2 *bp, vec2 crash[2])
Checks to see if a circle collides with a polygon.
Definition collision.c:862
int CollideLinePolygon(const vec2 *ap, double ad, double al, const CollPolyView *bt, const vec2 *bp, vec2 crash[2])
Checks to see if a line collides with a polygon.
Definition collision.c:729
int CollideLineLine(double s1x, double s1y, double e1x, double e1y, double s2x, double s2y, double e2x, double e2y, vec2 *crash)
Checks to see if two lines collide.
Definition collision.c:526
void poly_load(CollPoly *polygon, xmlNodePtr base, const char *name)
Loads a polygon from an xml node.
Definition collision.c:37
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)
Checks whether or not two sprites collide.
Definition collision.c:148
static int LineOnPolygon(const CollPolyView *at, const vec2 *ap, float x1, float y1, float x2, float y2, vec2 *crash)
Checks whether or not a line intersects a polygon.
Definition collision.c:486
double CollideCircleIntersection(const vec2 *p1, double r1, const vec2 *p2, double r2)
Calculates the area of intersection between two circles.
Definition collision.c:1159
int CollidePolygon(const CollPolyView *at, const vec2 *ap, const CollPolyView *bt, const vec2 *bp, vec2 *crash)
Checks whether or not two polygons collide. /!\ The function is not symmetric: the points of polygon ...
Definition collision.c:311
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:39
#define pow2(x)
Definition naev.h:53
#define FABS(x)
Definition naev.h:34
#define MAX(x, y)
Definition naev.h:37
int gl_isTrans(const glTexture *t, const int x, const int y)
Checks to see if a pixel is transparent in a texture.
Definition opengl_tex.c:957
static const double c[]
Definition rng.c:256
static const double d[]
Definition rng.c:263
static cholmod_common C
Definition safelanes.c:106
Represents a polygon used for collision detection.
Definition collision.h:13
float * x
Definition collision.h:14
float * y
Definition collision.h:15
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:43
double sw
Definition opengl_tex.h:53
uint8_t * trans
Definition opengl_tex.h:60
double sh
Definition opengl_tex.h:54
char * name
Definition opengl_tex.h:44
double sy
Definition opengl_tex.h:52
Definition msgcat.c:196
Represents a 2d vector.
Definition vec2.h:45
double y
Definition vec2.h:47
double x
Definition vec2.h:46