Around episode 50 Casey implements line segment intersection collisions for axis aligned rectangles.

His implementation consists of this TestWall function:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | internal bool32 TestWall(real32 WallX, real32 RelX, real32 RelY, real32 PlayerDeltaX, real32 PlayerDeltaY, real32 *tMin, real32 MinY, real32 MaxY) { bool32 Hit = false; real32 tEpsilon = 0.00001f; if(PlayerDeltaX != 0.0f) { real32 tResult = (WallX - RelX) / PlayerDeltaX; real32 Y = RelY + tResult*PlayerDeltaY; if((tResult >= 0.0f) && (*tMin > tResult)) { if((Y >= MinY) && (Y <= MaxY)) { *tMin = Maximum(0.0f, tResult - tEpsilon); Hit = true; } } } return(Hit); } |

And this iteration loop which tests the four edges and updates the tMin variable when collisions are found:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | real32 tRemaining = 1.0f; for(uint32 Iteration = 0; (Iteration < 4) && (tRemaining > 0.0f); ++Iteration) { real32 tMin = 1.0f; v2 WallNormal = {}; Assert((MaxTileX - MinTileX) < 32); Assert((MaxTileY - MinTileY) < 32); for(uint32 AbsTileY = MinTileY; AbsTileY <= MaxTileY; ++AbsTileY) { for(uint32 AbsTileX = MinTileX; AbsTileX <= MaxTileX; ++AbsTileX) { tile_map_position TestTileP = CenteredTilePoint(AbsTileX, AbsTileY, AbsTileZ); uint32 TileValue = GetTileValue(TileMap, TestTileP); if(!IsTileValueEmpty(TileValue)) { real32 DiameterW = TileMap->TileSideInMeters + Entity->Width; real32 DiameterH = TileMap->TileSideInMeters + Entity->Height; v2 MinCorner = -0.5f*v2{DiameterW, DiameterH}; v2 MaxCorner = 0.5f*v2{DiameterW, DiameterH}; tile_map_difference RelOldPlayerP = Subtract(TileMap, &Entity->P, &TestTileP); v2 Rel = RelOldPlayerP.dXY; if(TestWall(MinCorner.X, Rel.X, Rel.Y, PlayerDelta.X, PlayerDelta.Y, &tMin, MinCorner.Y, MaxCorner.Y)) { WallNormal = v2{-1, 0}; } if(TestWall(MaxCorner.X, Rel.X, Rel.Y, PlayerDelta.X, PlayerDelta.Y, &tMin, MinCorner.Y, MaxCorner.Y)) { WallNormal = v2{1, 0}; } if(TestWall(MinCorner.Y, Rel.Y, Rel.X, PlayerDelta.Y, PlayerDelta.X, &tMin, MinCorner.X, MaxCorner.X)) { WallNormal = v2{0, -1}; } if(TestWall(MaxCorner.Y, Rel.Y, Rel.X, PlayerDelta.Y, PlayerDelta.X, &tMin, MinCorner.X, MaxCorner.X)) { WallNormal = v2{0, 1}; } } } } Entity->P = Offset(TileMap, Entity->P, tMin*PlayerDelta); Entity->dP = Entity->dP - 1*Inner(Entity->dP, WallNormal)*WallNormal; PlayerDelta = PlayerDelta - 1*Inner(PlayerDelta, WallNormal)*WallNormal; tRemaining -= tMin*tRemaining; } |

Later on, on episodes 90 and 91 Casey implements rendering of rotated and scaled rectangles and he mentions that we could use that to enhance the collision detection.

In his implementation, Casey tests each pixel to see if it is inside a rotated rectangle:

1 2 3 4 5 6 7 8 9 10 11 12 | real32 Edge0 = Inner(PixelP - Origin, -Perp(XAxis)); real32 Edge1 = Inner(PixelP - (Origin + XAxis), -Perp(YAxis)); real32 Edge2 = Inner(PixelP - (Origin + XAxis + YAxis), Perp(XAxis)); real32 Edge3 = Inner(PixelP - (Origin + YAxis), Perp(YAxis)); if((Edge0 < 0) && (Edge1 < 0) && (Edge2 < 0) && (Edge3 < 0)) { *Pixel = Color32; } |

My question is, how to adapt the first two functions to use these better edge tests and find the appropriate tMin value?