diff --git a/data/map project.tiled-session b/data/map project.tiled-session index 52f03e9..d3c06f1 100644 --- a/data/map project.tiled-session +++ b/data/map project.tiled-session @@ -1,8 +1,8 @@ { - "activeFile": "map.tmj", + "activeFile": "overworld.tsx", "expandedProjectPaths": [ - "templates", - "." + ".", + "templates" ], "fileStates": { ":/automap-tiles.tsx": { @@ -13,11 +13,11 @@ "scaleInEditor": 1 }, "map.tmj": { - "scale": 4, - "selectedLayer": 2, + "scale": 3, + "selectedLayer": 0, "viewCenter": { - "x": 6599.375, - "y": 6795.125 + "x": 6520.166666666666, + "y": 6836.833333333333 } }, "overworld.tsx": { @@ -35,8 +35,8 @@ "project": "map project.tiled-project", "property.type": "int", "recentFiles": [ - "overworld.tsx", "map.tmj", + "overworld.tsx", "entities.tsx" ], "tileset.lastUsedFilter": "Tiled tileset files (*.tsx *.xml)", diff --git a/src/dusk/entity/entity.c b/src/dusk/entity/entity.c index fdb75bf..075a6ce 100644 --- a/src/dusk/entity/entity.c +++ b/src/dusk/entity/entity.c @@ -101,9 +101,8 @@ void entityUpdate(entity_t *entity) { switch(TILE_META_DATA[tile].solidType) { case TILE_SOLID_FULL: collision = physicsCheckCircleAABB( - newX, newY, selfCircR, - fx248Mulfx248(x, FIXED248(TILE_WIDTH_HEIGHT, 0)), - fx248Mulfx248(y, FIXED248(TILE_WIDTH_HEIGHT, 0)), + fx248Addfx248(newX, halfTileWH), fx248Addfx248(newY, halfTileWH), selfCircR, + lx, ty, FIXED248(TILE_WIDTH_HEIGHT, 0), FIXED248(TILE_WIDTH_HEIGHT, 0) ); @@ -111,44 +110,38 @@ void entityUpdate(entity_t *entity) { case TILE_SOLID_TRIANGLE_TOP_RIGHT: collision = physicsCheckCircleTriangle( - newX, newY, selfCircR, - lx, ty, + fx248Addfx248(newX, halfTileWH), fx248Addfx248(newY, halfTileWH), selfCircR, + rx, by, rx, ty, - rx, by + lx, ty ); break; - // case TILE_SOLID_TRIANGLE_TOP_LEFT: - // collision = physicsCheckCircleTriangle( - // newX, newY, selfCircR, - // lx, ty, - // rx, ty, - // lx, by - // ); - // break; - // case TILE_SOLID_TRIANGLE_BOTTOM_RIGHT: - // collision = physicsCheckCircleTriangle( - // newX, newY, selfCircR, - // fx248Mulfx248(x, FIXED248(TILE_WIDTH_HEIGHT, 0)), - // fx248Addfx248(fx248Mulfx248(y, FIXED248(TILE_WIDTH_HEIGHT, 0)), FIXED248(TILE_WIDTH_HEIGHT, 0)), - // fx248Addfx248(fx248Mulfx248(x, FIXED248(TILE_WIDTH_HEIGHT, 0)), FIXED248(TILE_WIDTH_HEIGHT, 0)), - // fx248Addfx248(fx248Mulfx248(y, FIXED248(TILE_WIDTH_HEIGHT, 0)), FIXED248(TILE_WIDTH_HEIGHT, 0)), - // fx248Mulfx248(x, FIXED248(TILE_WIDTH_HEIGHT, 0)), - // fx248Mulfx248(y, FIXED248(TILE_WIDTH_HEIGHT, 0)) - // ); - // break; + case TILE_SOLID_TRIANGLE_TOP_LEFT: + collision = physicsCheckCircleTriangle( + fx248Addfx248(newX, halfTileWH), fx248Addfx248(newY, halfTileWH), selfCircR, + lx, by, + lx, ty, + rx, ty + ); + break; - // case TILE_SOLID_TRIANGLE_BOTTOM_LEFT: - // collision = physicsCheckCircleTriangle( - // newX, newY, selfCircR, - // fx248Mulfx248(x, FIXED248(TILE_WIDTH_HEIGHT, 0)), - // fx248Addfx248(fx248Mulfx248(y, FIXED248(TILE_WIDTH_HEIGHT, 0)), FIXED248(TILE_WIDTH_HEIGHT, 0)), - // fx248Addfx248(fx248Mulfx248(x, FIXED248(TILE_WIDTH_HEIGHT, 0)), FIXED248(TILE_WIDTH_HEIGHT, 0)), - // fx248Addfx248(fx248Mulfx248(y, FIXED248(TILE_WIDTH_HEIGHT, 0)), FIXED248(TILE_WIDTH_HEIGHT, 0)), - // fx248Addfx248(fx248Mulfx248(x, FIXED248(TILE_WIDTH_HEIGHT, 0)), FIXED248(TILE_WIDTH_HEIGHT, 0)), - // fx248Mulfx248(y, FIXED248(TILE_WIDTH_HEIGHT, 0)) - // ); - // break; + case TILE_SOLID_TRIANGLE_BOTTOM_RIGHT: + collision = physicsCheckCircleTriangle( + fx248Addfx248(newX, halfTileWH), fx248Addfx248(newY, halfTileWH), selfCircR, + rx, ty, + rx, by, + lx, by + ); + break; + case TILE_SOLID_TRIANGLE_BOTTOM_LEFT: + collision = physicsCheckCircleTriangle( + fx248Addfx248(newX, halfTileWH), fx248Addfx248(newY, halfTileWH), selfCircR, + lx, ty, + lx, by, + rx, by + ); + break; default: continue; diff --git a/src/dusk/physics/physics.c b/src/dusk/physics/physics.c index 8f4dab3..2bd3a2d 100644 --- a/src/dusk/physics/physics.c +++ b/src/dusk/physics/physics.c @@ -99,13 +99,155 @@ collisionresult_t physicsCheckCircleAABB( return result; } +void physicsClosestPointOnSegment( + fixed248_t ax, fixed248_t ay, + fixed248_t bx, fixed248_t by, + fixed248_t px, fixed248_t py, + fixed248_t *outX, fixed248_t *outY +) { + fixed248_t abx = fx248Subfx248(bx, ax); + fixed248_t aby = fx248Subfx248(by, ay); + fixed248_t apx = fx248Subfx248(px, ax); + fixed248_t apy = fx248Subfx248(py, ay); + + fixed248_t abLenSq = fx248Addfx248( + fx248Mulfx248(abx, abx), + fx248Mulfx248(aby, aby) + ); + + if(abLenSq == FIXED248_ZERO) { + *outX = ax; + *outY = ay; + return; + } + + fixed248_t t = fx248Divfx248( + fx248Addfx248(fx248Mulfx248(apx, abx), fx248Mulfx248(apy, aby)), + abLenSq + ); + + if(t < FIXED248_ZERO) t = FIXED248_ZERO; + if(t > FIXED248_ONE) t = FIXED248_ONE; + + *outX = fx248Addfx248(ax, fx248Mulfx248(abx, t)); + *outY = fx248Addfx248(ay, fx248Mulfx248(aby, t)); +} + +bool_t physicsIsPointInTriangle( + fixed248_t px, fixed248_t py, + fixed248_t ax, fixed248_t ay, + fixed248_t bx, fixed248_t by, + fixed248_t cx, fixed248_t cy +) { + fixed248_t abx = fx248Subfx248(bx, ax); + fixed248_t aby = fx248Subfx248(by, ay); + fixed248_t bcx = fx248Subfx248(cx, bx); + fixed248_t bcy = fx248Subfx248(cy, by); + fixed248_t cax = fx248Subfx248(ax, cx); + fixed248_t cay = fx248Subfx248(ay, cy); + + fixed248_t apx = fx248Subfx248(px, ax); + fixed248_t apy = fx248Subfx248(py, ay); + fixed248_t bpx = fx248Subfx248(px, bx); + fixed248_t bpy = fx248Subfx248(py, by); + fixed248_t cpx = fx248Subfx248(px, cx); + fixed248_t cpy = fx248Subfx248(py, cy); + + fixed248_t cross1 = fx248Subfx248( + fx248Mulfx248(abx, apy), + fx248Mulfx248(aby, apx) + ); + fixed248_t cross2 = fx248Subfx248( + fx248Mulfx248(bcx, bpy), + fx248Mulfx248(bcy, bpx) + ); + fixed248_t cross3 = fx248Subfx248( + fx248Mulfx248(cax, cpy), + fx248Mulfx248(cay, cpx) + ); + + bool_t hasNeg = ( + (cross1 < FIXED248_ZERO) || + (cross2 < FIXED248_ZERO) || + (cross3 < FIXED248_ZERO) + ); + + bool_t hasPos = ( + (cross1 > FIXED248_ZERO) || + (cross2 > FIXED248_ZERO) || + (cross3 > FIXED248_ZERO) + ); + + return !(hasNeg && hasPos); +} + collisionresult_t physicsCheckCircleTriangle( fixed248_t circleX, fixed248_t circleY, fixed248_t circleR, fixed248_t triX0, fixed248_t triY0, fixed248_t triX1, fixed248_t triY1, fixed248_t triX2, fixed248_t triY2 ) { - collisionresult_t result; - result.hit = false; + collisionresult_t result = { .hit = false }; + fixed248_t vx[3] = { triX0, triX1, triX2 }; + fixed248_t vy[3] = { triY0, triY1, triY2 }; + + fixed248_t closestX = FIXED248_ZERO; + fixed248_t closestY = FIXED248_ZERO; + fixed248_t normalX = FIXED248_ZERO; + fixed248_t normalY = FIXED248_ZERO; + fixed248_t minDistSq = FIXED248_MAX; + + for(uint8_t i = 0; i < 3; ++i) { + uint8_t j = (i + 1) % 3; + + fixed248_t testX, testY; + physicsClosestPointOnSegment( + vx[i], vy[i], vx[j], vy[j], + circleX, circleY, &testX, &testY + ); + + fixed248_t dx = fx248Subfx248(circleX, testX); + fixed248_t dy = fx248Subfx248(circleY, testY); + fixed248_t distSq = fx248Addfx248( + fx248Mulfx248(dx, dx), + fx248Mulfx248(dy, dy) + ); + + if(distSq < minDistSq) { + minDistSq = distSq; + closestX = testX; + closestY = testY; + normalX = dx; + normalY = dy; + } + } + + fixed248_t dist = fx248Sqrt(minDistSq); + fixed248_t invDist = ( + (dist != FIXED248_ZERO) ? + fx248Divfx248(FIXED248_ONE, dist) : + FIXED248_ONE + ); + + fixed248_t nx = fx248Mulfx248(-normalX, invDist); + fixed248_t ny = fx248Mulfx248(-normalY, invDist); + + if(physicsIsPointInTriangle( + circleX, circleY, vx[0], vy[0], vx[1], vy[1], vx[2], vy[2] + )) { + result.hit = true; + result.depth = fx248Subfx248(circleR, dist); + result.normalX = nx; + result.normalY = ny; + return result; + } + + if(dist < circleR) { + result.hit = true; + result.depth = fx248Subfx248(circleR, dist); + result.normalX = nx; + result.normalY = ny; + } + return result; -} \ No newline at end of file +} diff --git a/src/dusk/physics/physics.h b/src/dusk/physics/physics.h index fedfa20..b8d2aec 100644 --- a/src/dusk/physics/physics.h +++ b/src/dusk/physics/physics.h @@ -48,6 +48,45 @@ collisionresult_t physicsCheckCircleAABB( fixed248_t aabbWidth, fixed248_t aabbHeight ); +/** + * Calculate the closest point on a line segment to a point. + * + * @param ax X coordinate of the first endpoint of the segment. + * @param ay Y coordinate of the first endpoint of the segment. + * @param bx X coordinate of the second endpoint of the segment. + * @param by Y coordinate of the second endpoint of the segment. + * @param px X coordinate of the point. + * @param py Y coordinate of the point. + * @param outX Pointer to store the X coordinate of the closest point. + * @param outY Pointer to store the Y coordinate of the closest point. + */ +void physicsClosestPointOnSegment( + fixed248_t ax, fixed248_t ay, + fixed248_t bx, fixed248_t by, + fixed248_t px, fixed248_t py, + fixed248_t *outX, fixed248_t *outY +); + +/** + * Check if a point is inside a triangle defined by three vertices. + * + * @param px X coordinate of the point. + * @param py Y coordinate of the point. + * @param x0 X coordinate of the first vertex of the triangle. + * @param y0 Y coordinate of the first vertex of the triangle. + * @param x1 X coordinate of the second vertex of the triangle. + * @param y1 Y coordinate of the second vertex of the triangle. + * @param x2 X coordinate of the third vertex of the triangle. + * @param y2 Y coordinate of the third vertex of the triangle. + * @return true if the point is inside the triangle, false otherwise. + */ +bool_t physicsIsPointInTriangle( + fixed248_t px, fixed248_t py, + fixed248_t x0, fixed248_t y0, + fixed248_t x1, fixed248_t y1, + fixed248_t x2, fixed248_t y2 +); + /** * Check for collision between a circle and a triangle. * diff --git a/src/dusk/util/fixed.h b/src/dusk/util/fixed.h index bd8c4c3..956e047 100644 --- a/src/dusk/util/fixed.h +++ b/src/dusk/util/fixed.h @@ -19,6 +19,7 @@ typedef int32_t fixed248_t; (((f) * FIXED248_HIGH_MULTIPLIER) / 100) \ )) #define FIXED248_ONE (FIXED248(1, 0)) +#define FIXED248_ZERO (FIXED248(0, 0)) /** * Convert an int32_t value to a fixed248_t value.