Significantly speed up the collision detection for Pixel Perfect

This commit is contained in:
Sylvain PILLOT 2023-08-25 09:52:01 +02:00
parent 0994a32c23
commit 50b54afc2f
6 changed files with 278 additions and 44 deletions

View File

@ -8,6 +8,7 @@ include(GenerateG1A)
include(GenerateG3A)
include(Fxconv)
find_package(Gint 2.9 REQUIRED)
find_package(LibProf 2.4 REQUIRED)
set(SOURCES
src/main.cpp
@ -32,7 +33,7 @@ fxconv_declare_assets(${ASSETS} ${ASSETS_fx} ${ASSETS_cg} WITH_METADATA)
add_executable(myaddin ${SOURCES} ${ASSETS} ${ASSETS_${FXSDK_PLATFORM}})
target_compile_options(myaddin PRIVATE -Wall -Wextra -Os -std=c++20)
target_link_libraries(myaddin Gint::Gint)
target_link_libraries(myaddin LibProf::LibProf Gint::Gint -lstdc++)
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
generate_g1a(TARGET myaddin OUTPUT "MyAddin.g1a"

Binary file not shown.

View File

@ -1,6 +1,6 @@
mainship1.png:
type: bopti-image
profile: rgb565a
profile: p8_rgb565a
name: img_player
Boss1.png:

View File

@ -2,6 +2,9 @@
#include "extrakeyboard.h"
#include "utilities.h"
#include <libprof.h>
#include <fxlibc/printf.h>
#include <stdbool.h>
@ -53,23 +56,28 @@ void update()
int main(void)
{
/* */
uint32_t time_AABB=0, time_FastPP=0, time_DebugPP=0;
prof_t perf_AABB, perf_FastPP, perf_DebugPP;
prof_init();
__printf_enable_fp();
xmin[0] = 10;
ymin[0] = 10;
xmin[1] = DWIDTH/2 - img_rock.width/2;
ymin[1] = (DHEIGHT-50)/2 - img_rock.height/2;
sprite1 = { .x = xmin[0],
.y = ymin[0],
.image = &img_player };
sprite2 = { .x = xmin[1],
.y = ymin[1],
.image = &img_rock };
sprite1 = { .x = xmin[0], .y = ymin[0], .image = &img_player };
sprite2 = { .x = xmin[1], .y = ymin[1], .image = &img_rock };
update();
bool AABB = false;
bool FastPP = false;
bool DebugPP = false;
do
{
@ -85,21 +93,58 @@ int main(void)
dimage( xmin[1], ymin[1], &img_rock );
drect_border( xmin[1], ymin[1], xmax[1], ymax[1], C_NONE, 1, C_WHITE );
dtext( 50, DHEIGHT-40, C_WHITE, "AABB Collide");
if (AABB_Collision( sprite1, sprite2 )) drect( 10, DHEIGHT-40, 40, DHEIGHT-10, C_RED);
perf_AABB = prof_make();
prof_enter(perf_AABB);
AABB = AABB_Collision( sprite1, sprite2 );
prof_leave(perf_AABB);
time_AABB = prof_time(perf_AABB);
perf_FastPP = prof_make();
prof_enter(perf_FastPP);
FastPP = Pixel_Perfect_Collision( sprite1, sprite2 );
prof_leave(perf_FastPP);
time_FastPP = prof_time(perf_FastPP);
perf_DebugPP = prof_make();
prof_enter(perf_DebugPP);
DebugPP = DEBUG_Pixel_Perfect_Collision( sprite1, sprite2 );
prof_leave(perf_DebugPP);
time_DebugPP = prof_time(perf_DebugPP);
dtext( 50, DHEIGHT-35, C_WHITE, "AABB");
if (AABB) drect( 10, DHEIGHT-40, 40, DHEIGHT-10, C_RED);
else drect( 10, DHEIGHT-40, 40, DHEIGHT-10, C_GREEN );
//dprint( 10, DHEIGHT-35, C_WHITE, "%.3f", (float) time_AABB / 1000.0f);
dprint( 10, DHEIGHT-35, C_WHITE, "%d", time_AABB);
dtext( 200, DHEIGHT-40, C_WHITE, "Pixel Perfect Collide");
if (Pixel_Perfect_Collision( sprite1, sprite2 )) drect( DWIDTH-40, DHEIGHT-40, DWIDTH-10, DHEIGHT-10, C_RED);
dtext( 215, DHEIGHT-35, C_WHITE, " Pixel Perfect");
dtext( 215, DHEIGHT-20, C_WHITE, "<DEBUG vs Fast>");
if (FastPP) drect( DWIDTH-40, DHEIGHT-40, DWIDTH-10, DHEIGHT-10, C_RED);
else drect( DWIDTH-40, DHEIGHT-40, DWIDTH-10, DHEIGHT-10, C_GREEN );
//dprint( DWIDTH-40, DHEIGHT-35, C_WHITE, "%.3f", (float) time_FastPP / 1000.0f);
dprint( DWIDTH-40, DHEIGHT-35, C_WHITE, "%d", time_FastPP);
if (DebugPP) drect( DWIDTH/2-15, DHEIGHT-40, DWIDTH/2+15, DHEIGHT-10, C_RED);
else drect( DWIDTH/2-15, DHEIGHT-40, DWIDTH/2+15, DHEIGHT-10, C_GREEN );
//dprint( DWIDTH/2-15, DHEIGHT-35, C_WHITE, "%.3f", (float) time_DebugPP / 1000.0f);
dprint( DWIDTH/2-15, DHEIGHT-35, C_WHITE, "%d", time_DebugPP);
dupdate();
}
while (exit==false);
prof_quit();
return 1;
}

View File

@ -14,6 +14,8 @@
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
bool AABB_Collision( SpriteLocator image1, SpriteLocator image2 )
{
if( (image2.x >= image1.x + image1.image->width)
@ -27,14 +29,14 @@ bool AABB_Collision( SpriteLocator image1, SpriteLocator image2 )
}
bool Pixel_Perfect_Collision( SpriteLocator image1, SpriteLocator image2 )
bool DEBUG_Pixel_Perfect_Collision( SpriteLocator image1, SpriteLocator image2 )
{
if( !AABB_Collision( image1, image2 ) )
return false; // bounding box not overlapping, impossible to have collision
return false; // bounding boxes not overlapping, impossible to have collision
/* if we reach that point, this means that we need to perform pixel perfect collsion detection */
/* First we will calculate the corners of the zone to be tested for collision */
/* as it is not usefull to check for all the surface of the image, only the */
/* overlapping area will be verified */
@ -102,8 +104,10 @@ bool Pixel_Perfect_Collision( SpriteLocator image1, SpriteLocator image2 )
&& (data_u16_2[ bdeltax + i ] != transp2 ) )
return true;
}
data_u16_1 += image1.image->width;
data_u16_2 += image2.image->width;
data1 += image1.image->stride;
data_u16_1 = (uint16_t *) data1;
data2 += image2.image->stride;
data_u16_2 = (uint16_t *) data2;
}
return false;
}
@ -116,8 +120,8 @@ bool Pixel_Perfect_Collision( SpriteLocator image1, SpriteLocator image2 )
void *data1 = image1.image->data + adeltay * image1.image->stride;
void *data2 = image2.image->data + bdeltay * image2.image->stride;
uint8_t *data_u8_1 = (uint8_t *) data1;
uint8_t *data_u8_2 = (uint8_t *) data2;
int8_t *data_u8_1 = (int8_t *) data1;
int8_t *data_u8_2 = (int8_t *) data2;
int transp1 = image_alpha(image1.image->format);
int transp2 = image_alpha(image2.image->format);
@ -130,14 +134,15 @@ bool Pixel_Perfect_Collision( SpriteLocator image1, SpriteLocator image2 )
&& (data_u8_2[ bdeltax + i ] != transp2 ) )
return true;
}
data_u8_1 += image1.image->width;
data_u8_2 += image2.image->width;
data1 += image1.image->stride;
data_u8_1 = (int8_t *) data1;
data2 += image2.image->stride;
data_u8_2 = (int8_t *) data2;
}
return false;
}
/* for the other cases, we need to use masks as we cannot directly compare the values contained in the data member */
uint8_t *mask1 = (uint8_t *) malloc( rows * columns );
@ -155,6 +160,8 @@ bool Pixel_Perfect_Collision( SpriteLocator image1, SpriteLocator image2 )
int transp1 = image_alpha(image1.image->format);
int transp2 = image_alpha(image2.image->format);
if (image1.image->format == IMAGE_RGB565 || image1.image->format == IMAGE_RGB565A )
{
for( int j=0; j<rows; j++)
@ -163,9 +170,24 @@ bool Pixel_Perfect_Collision( SpriteLocator image1, SpriteLocator image2 )
{
mask1[j*columns+i] = data_u16_1[ adeltax + i ] == transp1 ? 0 : 1;
}
data_u16_1 += image1.image->width;
data1 += image1.image->stride;
data_u16_1 = (uint16_t *) data1;
}
}
else if (image1.image->format == IMAGE_P8_RGB565 || image1.image->format == IMAGE_P8_RGB565A )
{
for( int j=0; j<rows; j++)
{
for (int i=0; i<columns; i++)
{
mask1[j*columns+i] = data_u8_1[ adeltax + i ] == transp1 ? 0 : 1;
}
data1 += image1.image->stride;
data_u8_1 = (int8_t *) data1;
}
}
if (image2.image->format == IMAGE_RGB565 || image2.image->format == IMAGE_RGB565A )
{
@ -175,23 +197,11 @@ bool Pixel_Perfect_Collision( SpriteLocator image1, SpriteLocator image2 )
{
mask2[j*columns+i] = data_u16_2[ bdeltax + i ] == transp2 ? 0 : 1;
}
data_u16_2 += image2.image->width;
data2 += image2.image->stride;
data_u16_2 = (uint16_t *) data2;
}
}
if (image1.image->format == IMAGE_P8_RGB565 || image1.image->format == IMAGE_P8_RGB565A )
{
for( int j=0; j<rows; j++)
{
for (int i=0; i<columns; i++)
{
mask1[j*columns+i] = data_u8_1[ adeltax + i ] == transp1 ? 0 : 1;
}
data_u8_1 += image1.image->width;
}
}
if (image2.image->format == IMAGE_P8_RGB565 || image2.image->format == IMAGE_P8_RGB565A )
else if (image2.image->format == IMAGE_P8_RGB565 || image2.image->format == IMAGE_P8_RGB565A )
{
for( int j=0; j<rows; j++)
{
@ -199,12 +209,16 @@ bool Pixel_Perfect_Collision( SpriteLocator image1, SpriteLocator image2 )
{
mask2[j*columns+i] = data_u8_2[ bdeltax + i ] == transp2 ? 0 : 1;
}
data_u8_2 += image2.image->width;
data2 += image2.image->stride;
data_u8_2 = (int8_t *) data2;
}
}
/*
drect( 0, 0, columns+2, rows+2, C_BLUE );
drect( DWIDTH-columns-2, 0, DWIDTH, rows+2, C_BLUE );
*/
for( int j=0; j<rows; j++)
{
@ -230,5 +244,176 @@ bool Pixel_Perfect_Collision( SpriteLocator image1, SpriteLocator image2 )
free( mask1 );
free( mask2 );
return false;
}
bool Pixel_Perfect_Collision( SpriteLocator image1, SpriteLocator image2 )
{
if( !AABB_Collision( image1, image2 ) )
return false; // bounding boxes not overlapping, impossible to have collision
/* if we reach that point, this means that we need to perform pixel perfect collsion detection */
/* First we will calculate the corners of the zone to be tested for collision */
/* as it is not usefull to check for all the surface of the image, only the */
/* overlapping area will be verified */
/* WARNING !! */
/* P4 format is not supported yet has it is currently focused on use with Azur */
/* that is mostly P8/RGB565 oriented for ultra fast performances */
if ( IMAGE_IS_P4(image1.image->format) || IMAGE_IS_P4(image2.image->format) )
return false;
int astartx, aendx;
int astarty, aendy;
int adeltax, adeltay;
int bstartx, bendx;
int bstarty, bendy;
int bdeltax, bdeltay;
int rows, columns;
if (image1.x <= image2.x)
{
adeltax = image2.x - image1.x;
columns = MIN(image1.image->width - adeltax, image2.image->width);
bdeltax = 0;
}
else
{
bdeltax = image1.x - image2.x;
columns = MIN(image2.image->width - bdeltax, image1.image->width);
adeltax = 0;
}
if (image1.y <= image2.y)
{
adeltay = image2.y - image1.y;
rows = MIN(image1.image->height - adeltay, image2.image->height);
bdeltay = 0;
}
else
{
bdeltay = image1.y - image2.y;
rows = MIN(image2.image->height - bdeltay, image1.image->height);
adeltay = 0;
}
/* if BOTH images are in a 16bits color format, we will not use masks and will quicken the process */
if ( IMAGE_IS_RGB16(image1.image->format) && IMAGE_IS_RGB16(image2.image->format) )
{
void *data1 = image1.image->data + adeltay * image1.image->stride;
void *data2 = image2.image->data + bdeltay * image2.image->stride;
uint16_t *data_u16_1 = (uint16_t *) data1;
uint16_t *data_u16_2 = (uint16_t *) data2;
int transp1 = image_alpha(image1.image->format);
int transp2 = image_alpha(image2.image->format);
for( int j=0; j<rows; j++)
{
for (int i=0; i<columns; i++)
{
if( (data_u16_1[ adeltax + i ] != transp1 )
&& (data_u16_2[ bdeltax + i ] != transp2 ) )
return true;
}
data1 += image1.image->stride;
data_u16_1 = (uint16_t *) data1;
data2 += image2.image->stride;
data_u16_2 = (uint16_t *) data2;
}
return false;
}
/* if BOTH images are in a 8bits color format, we will not use masks and will quicken the process */
if ( IMAGE_IS_P8(image1.image->format) && IMAGE_IS_P8(image2.image->format) )
{
void *data1 = image1.image->data + adeltay * image1.image->stride;
void *data2 = image2.image->data + bdeltay * image2.image->stride;
int8_t *data_u8_1 = (int8_t *) data1;
int8_t *data_u8_2 = (int8_t *) data2;
int transp1 = image_alpha(image1.image->format);
int transp2 = image_alpha(image2.image->format);
for( int j=0; j<rows; j++)
{
for (int i=0; i<columns; i++)
{
if( (data_u8_1[ adeltax + i ] != transp1 )
&& (data_u8_2[ bdeltax + i ] != transp2 ) )
return true;
}
data1 += image1.image->stride;
data_u8_1 = (int8_t *) data1;
data2 += image2.image->stride;
data_u8_2 = (int8_t *) data2;
}
return false;
}
/* if we reach that point, this means that images have differnt format and we need to manage this */
uint8_t d1;
uint8_t d2;
void *data1 = image1.image->data + adeltay * image1.image->stride;
void *data2 = image2.image->data + bdeltay * image2.image->stride;
int8_t *data_u8_1 = (int8_t *) data1;
uint16_t *data_u16_1 = (uint16_t *) data1;
int8_t *data_u8_2 = (int8_t *) data2;
uint16_t *data_u16_2 = (uint16_t *) data2;
int transp1 = image_alpha(image1.image->format);
int transp2 = image_alpha(image2.image->format);
bool im1_P16 = IMAGE_IS_RGB16(image1.image->format);
bool im1_P8 = IMAGE_IS_P8(image1.image->format);
bool im2_P16 = IMAGE_IS_RGB16(image2.image->format);
bool im2_P8 = IMAGE_IS_P8(image2.image->format);
for( int j=0; j<rows; j++)
{
for (int i=0; i<columns; i++)
{
/* d1 is set to 1 if pixel of image1 is not transparent and to 0 if transparent */
/* need to be format dependant here so quite time consumming test at each loop :( )*/
if (im1_P16)
d1 = data_u16_1[ adeltax + i ] == transp1 ? 0 : 1;
else if (im1_P8)
d1 = data_u8_1[ adeltax + i ] == transp1 ? 0 : 1;
/* d2 is set to 1 if pixel of image2 is not transparent and to 0 if transparent */
/* need to be format dependant here so quite time consumming test at each loop :( )*/
if (im2_P16)
d2 = data_u16_2[ bdeltax + i ] == transp2 ? 0 : 1;
else if (im2_P8)
d2 = data_u8_2[ bdeltax + i ] == transp2 ? 0 : 1;
/* if d1 + d2 = 2 means that both coincident pixels are not transparent and then we have collision*/
if (d1 + d2 == 2) return true;
}
/* we move the pointer to the next line of both images */
data1 += image1.image->stride;
data2 += image2.image->stride;
data_u8_1 = (int8_t *) data1;
data_u8_2 = (int8_t *) data2;
data_u16_1 = (uint16_t *) data1;
data_u16_2 = (uint16_t *) data2;
}
return false;
}

View File

@ -21,4 +21,7 @@ bool AABB_Collision( SpriteLocator image1, SpriteLocator image2 );
bool Pixel_Perfect_Collision( SpriteLocator image1, SpriteLocator image2 );
bool DEBUG_Pixel_Perfect_Collision( SpriteLocator image1, SpriteLocator image2 );
#endif