From 4d84d94166772adf112652becf90f07f38e47ff4 Mon Sep 17 00:00:00 2001 From: aap Date: Sun, 23 Jun 2019 19:59:58 +0200 Subject: more Radar code --- src/Radar.cpp | 366 +++++++++++++++++++++++++++++++++++++++++++++++++------ src/Radar.h | 8 +- src/RwHelper.cpp | 17 +++ src/RwHelper.h | 1 + 4 files changed, 352 insertions(+), 40 deletions(-) diff --git a/src/Radar.cpp b/src/Radar.cpp index 2dd471f1..90d27af2 100644 --- a/src/Radar.cpp +++ b/src/Radar.cpp @@ -1,5 +1,6 @@ #include "common.h" #include "patcher.h" +#include "RwHelper.h" #include "Radar.h" #include "Camera.h" #include "Hud.h" @@ -14,8 +15,6 @@ #include "Streaming.h" WRAPPER void CRadar::Draw3dMarkers() { EAXJMP(0x4A4C70); } -WRAPPER int CRadar::ClipRadarPoly(CVector2D *out, CVector2D *in) { EAXJMP(0x4A64A0); } -WRAPPER void CRadar::TransformRadarPointToRealWorldSpace(CVector2D &out, const CVector2D &in) { EAXJMP(0x4A5300); } float &CRadar::m_RadarRange = *(float*)0x8E281C; CBlip *CRadar::ms_RadarTrace = (CBlip*)0x6ED5E0; @@ -69,6 +68,8 @@ CSprite2d *CRadar::RadarSprites[RADAR_SPRITE_COUNT] = { }; #define RADAR_NUM_TILES (8) +#define RADAR_TILE_SIZE (WORLD_SIZE_X / RADAR_NUM_TILES) +static_assert(RADAR_TILE_SIZE == (WORLD_SIZE_Y / RADAR_NUM_TILES), "CRadar: not a square"); #define RADAR_MIN_RANGE (120.0f) #define RADAR_MAX_RANGE (350.0f) @@ -190,18 +191,221 @@ void CRadar::ClearBlipForEntity(eBlipType type, int32 id) } #endif -#if 1 +bool +IsPointInsideRadar(const CVector2D &point) +{ + if(point.x < -1.0f || point.x > 1.0f) return false; + if(point.y < -1.0f || point.y > 1.0f) return false; + return true; +} + +// clip line p1,p2 against (-1.0, 1.0) in x and y, set out to clipped point closest to p1 +int +LineRadarBoxCollision(CVector2D &out, const CVector2D &p1, const CVector2D &p2) +{ + float d1, d2; + float t; + float x, y; + float shortest = 1.0f; + int edge = -1; + + // clip against left edge, x = -1.0 + d1 = -1.0f - p1.x; + d2 = -1.0f - p2.x; + if(d1 * d2 < 0.0f){ + // they are on opposite sides, get point of intersection + t = d1 / (d1 - d2); + y = (p2.y - p1.y)*t + p1.y; + if(y >= -1.0f && y <= 1.0f && t <= shortest){ + out.x = -1.0f; + out.y = y; + edge = 3; + shortest = t; + } + } + + // clip against right edge, x = 1.0 + d1 = p1.x - 1.0f; + d2 = p2.x - 1.0f; + if(d1 * d2 < 0.0f){ + // they are on opposite sides, get point of intersection + t = d1 / (d1 - d2); + y = (p2.y - p1.y)*t + p1.y; + if(y >= -1.0f && y <= 1.0f && t <= shortest){ + out.x = 1.0f; + out.y = y; + edge = 1; + shortest = t; + } + } + + // clip against top edge, y = -1.0 + d1 = -1.0f - p1.y; + d2 = -1.0f - p2.y; + if(d1 * d2 < 0.0f){ + // they are on opposite sides, get point of intersection + t = d1 / (d1 - d2); + x = (p2.x - p1.x)*t + p1.x; + if(x >= -1.0f && x <= 1.0f && t <= shortest){ + out.y = -1.0f; + out.x = x; + edge = 0; + shortest = t; + } + } + + // clip against bottom edge, y = 1.0 + d1 = p1.y - 1.0f; + d2 = p2.y - 1.0f; + if(d1 * d2 < 0.0f){ + // they are on opposite sides, get point of intersection + t = d1 / (d1 - d2); + x = (p2.x - p1.x)*t + p1.x; + if(x >= -1.0f && x <= 1.0f && t <= shortest){ + out.y = 1.0f; + out.x = x; + edge = 2; + shortest = t; + } + } + + return edge; +} + +#if 0 +WRAPPER int CRadar::ClipRadarPoly(CVector2D *poly, const CVector2D *in) { EAXJMP(0x4A64A0); } +#else +// Why not a proper clipping algorithm? +int CRadar::ClipRadarPoly(CVector2D *poly, const CVector2D *rect) +{ + CVector2D corners[4] = { + { 1.0f, -1.0f }, // top right + { 1.0f, 1.0f }, // bottom right + { -1.0f, 1.0f }, // bottom left + { -1.0f, -1.0f }, // top left + }; + CVector2D tmp; + int i, j, n; + int laste, e, e1, e2;; + bool inside[4]; + + for(i = 0; i < 4; i++) + inside[i] = IsPointInsideRadar(rect[i]); + + laste = -1; + n = 0; + for(i = 0; i < 4; i++) + if(inside[i]){ + // point is inside, just add + poly[n++] = rect[i]; + }else{ + // point is outside but line to this point might be clipped + e1 = LineRadarBoxCollision(poly[n], rect[i], rect[(i+4-1) % 4]); + if(e1 != -1){ + laste = e1; + n++; + } + // and line from this point might be clipped as well + e2 = LineRadarBoxCollision(poly[n], rect[i], rect[(i+1) % 4]); + if(e2 != -1){ + if(e1 == -1){ + // if other line wasn't clipped, i.e. it was complete outside, + // we may have to insert another vertex if last clipped line + // was on a different edge + + // find the last intersection if we haven't seen it yet + if(laste == -1) + for(j = 3; j >= i; j--){ + // game uses an if here for j == 0 + e = LineRadarBoxCollision(tmp, rect[j], rect[(j+4-1) % 4]); + if(e != -1){ + laste = e; + break; + } + } + assert(laste != -1); + + // insert corners that were skipped + tmp = poly[n]; + for(e = laste; e != e2; e = (e+1) % 4) + poly[n++] = corners[e]; + poly[n] = tmp; + } + n++; + } + } + + if(n == 0){ + // If no points, either the rectangle is completely outside or completely surrounds the radar + // no idea what's going on here... + float m = (rect[0].y - rect[1].y) / (rect[0].x - rect[1].x); + if((m*rect[3].x - rect[3].y) * (m*rect[0].x - rect[0].y) < 0.0f){ + m = (rect[0].y - rect[3].y) / (rect[0].x - rect[3].x); + if((m*rect[1].x - rect[1].y) * (m*rect[0].x - rect[0].y) < 0.0f){ + poly[0] = corners[0]; + poly[1] = corners[1]; + poly[2] = corners[2]; + poly[3] = corners[3]; + n = 4; + } + } + } + + return n; +} +#endif + +#if 0 WRAPPER void CRadar::DrawRadarSection(int x, int y) { EAXJMP(0x4A67E0); } #else void CRadar::DrawRadarSection(int x, int y) { - + int i; + RwTexDictionary *txd; + CVector2D worldPoly[8]; + CVector2D radarCorners[4]; + CVector2D radarPoly[8]; + CVector2D texCoords[8]; + CVector2D screenPoly[8]; + int numVertices; + RwTexture *texture = nil; + + GetTextureCorners(x, y, worldPoly); + ClipRadarTileCoords(x, y); + + assert(CTxdStore::GetSlot(gRadarTxdIds[x + RADAR_NUM_TILES * y])); + txd = CTxdStore::GetSlot(gRadarTxdIds[x + RADAR_NUM_TILES * y])->texDict; + if(txd) + texture = GetFirstTexture(txd); + if(texture == nil) + return; + + for(i = 0; i < 4; i++) + TransformRealWorldPointToRadarSpace(radarCorners[i], worldPoly[i]); + + numVertices = ClipRadarPoly(radarPoly, radarCorners); + + // FIX: can return earlier here +// if(numVertices == 0) + if(numVertices < 3) + return; + + for(i = 0; i< numVertices; i++){ + TransformRadarPointToRealWorldSpace(worldPoly[i], radarPoly[i]); + TransformRealWorldToTexCoordSpace(texCoords[i], worldPoly[i], x, y); + TransformRadarPointToScreenSpace(screenPoly[i], radarPoly[i]); + } + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(texture)); + CSprite2d::SetVertices(numVertices, (float*)screenPoly, (float*)texCoords, CRGBA(255, 255, 255, 255)); + // check done above now +// if(numVertices > 2) + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::GetVertices(), numVertices); } #endif void CRadar::RequestMapSection(int x, int y) { - ClipRadarTileCoords(&x, &y); + ClipRadarTileCoords(x, y); CStreaming::RequestTxd(gRadarTxdIds[x + RADAR_NUM_TILES * y], STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_DEPENDENCY); } @@ -249,10 +453,10 @@ WRAPPER void CRadar::TransformRealWorldToTexCoordSpace(CVector2D &out, const CVe #else void CRadar::TransformRealWorldToTexCoordSpace(CVector2D &out, const CVector2D &in, int x, int y) { - out.x = in.x - (x * 500.0f + WORLD_MIN_X); - out.y = -(in.y - ((RADAR_NUM_TILES - y) * 500.0f + WORLD_MIN_Y)); - out.x /= 500.0f; - out.y /= 500.0f; + out.x = in.x - (x * RADAR_TILE_SIZE + WORLD_MIN_X); + out.y = -(in.y - ((RADAR_NUM_TILES - y) * RADAR_TILE_SIZE + WORLD_MIN_Y)); + out.x /= RADAR_TILE_SIZE; + out.y /= RADAR_TILE_SIZE; } #endif @@ -261,10 +465,13 @@ WRAPPER void CRadar::DrawRadarMap() { EAXJMP(0x4A6C20); } #else void CRadar::DrawRadarMap() { + // Game calculates an unused CRect here + DrawRadarMask(); - int x = floorf((vec2DRadarOrigin.x - WORLD_MIN_X) / 500.0f); - int y = round(7.0f - (vec2DRadarOrigin.y - WORLD_MIN_Y) / 500.0f); + // top left ist (0, 0) + int x = floorf((vec2DRadarOrigin.x - WORLD_MIN_X) / RADAR_TILE_SIZE); + int y = ceilf((RADAR_NUM_TILES-1) - (vec2DRadarOrigin.y - WORLD_MIN_Y) / RADAR_TILE_SIZE); StreamRadarSections(x, y); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); @@ -279,13 +486,13 @@ void CRadar::DrawRadarMap() RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void*)FALSE); DrawRadarSection(x - 1, y - 1); - DrawRadarSection(x, y - 1); + DrawRadarSection(x, y - 1); DrawRadarSection(x + 1, y - 1); DrawRadarSection(x - 1, y); - DrawRadarSection(x, y); + DrawRadarSection(x, y); DrawRadarSection(x + 1, y); DrawRadarSection(x - 1, y + 1); - DrawRadarSection(x, y + 1); + DrawRadarSection(x, y + 1); DrawRadarSection(x + 1, y + 1); } #endif @@ -490,14 +697,47 @@ void CRadar::TransformRealWorldPointToRadarSpace(CVector2D &out, const CVector2D s = sin(atan2(-forward.x, forward.y)); c = cos(atan2(-forward.x, forward.y)); } - + float x = (in.x - vec2DRadarOrigin.x) * (1.0f / m_RadarRange); float y = (in.y - vec2DRadarOrigin.y) * (1.0f / m_RadarRange); - + out.x = s * y + c * x; out.y = c * y - s * x; } -#endif +#endif + +#if 0 +WRAPPER void CRadar::TransformRadarPointToRealWorldSpace(CVector2D &out, const CVector2D &in) { EAXJMP(0x4A5300); } +#else +void CRadar::TransformRadarPointToRealWorldSpace(CVector2D &out, const CVector2D &in) +{ + float s, c; + + s = -sin(atan2(-TheCamera.GetForward().x, TheCamera.GetForward().y)); + c = cos(atan2(-TheCamera.GetForward().x, TheCamera.GetForward().y)); + + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN1 || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWNPED) { + s = 0.0f; + c = 1.0f; + } else if (TheCamera.GetLookDirection() != LOOKING_FORWARD) { + CVector forward; + + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FIRSTPERSON) { + forward = TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetForward(); + forward.Normalise(); // a bit useless... + } else + forward = TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetPosition() - TheCamera.Cams[TheCamera.ActiveCam].SourceBeforeLookBehind; + + s = -sin(atan2(-forward.x, forward.y)); + c = cos(atan2(-forward.x, forward.y)); + } + + out.x = s * in.y + c * in.x; + out.y = c * in.y - s * in.x; + + out = out*m_RadarRange + vec2DRadarOrigin; +} +#endif #if 0 WRAPPER void CRadar::DrawRadarSprite(int sprite, float x, float y, int alpha) { EAXJMP(0x4A5EF0); } @@ -554,13 +794,13 @@ void CRadar::DrawRotatingRadarSprite(CSprite2d* sprite, float x, float y, float curPosn[0].x = x - SCREEN_SCALE_X(5.6f); curPosn[0].y = y + SCREEN_SCALE_Y(5.6f); - + curPosn[1].x = x + SCREEN_SCALE_X(5.6f); curPosn[1].y = y + SCREEN_SCALE_Y(5.6f); - + curPosn[2].x = x - SCREEN_SCALE_X(5.6f); curPosn[2].y = y - SCREEN_SCALE_Y(5.6f); - + curPosn[3].x = x + SCREEN_SCALE_X(5.6f); curPosn[3].y = y - SCREEN_SCALE_Y(5.6f); @@ -590,31 +830,83 @@ bool CRadar::DisplayThisBlip(int counter) #if 0 WRAPPER void CRadar::GetTextureCorners(int x, int y, CVector2D *out) { EAXJMP(0x4A61C0); }; #else +// Transform from section indices to world coordinates void CRadar::GetTextureCorners(int x, int y, CVector2D *out) { - out[0].x = 500.0f * (x - 4); - out[0].y = 500.0f * (3 - y); - out[1].x = 500.0f * (y - 4 + 1); - out[1].y = 500.0f * (3 - y); - out[2].x = 500.0f * (y - 4 + 1); - out[2].y = 500.0f * (3 - y + 1); - out[3].x = 500.0f * (x - 4); - out[3].y = 500.0f * (3 - y + 1); + x = x - RADAR_NUM_TILES/2; + y = -(y - RADAR_NUM_TILES/2); + + // bottom left + out[0].x = RADAR_TILE_SIZE * (x); + out[0].y = RADAR_TILE_SIZE * (y - 1); + + // bottom right + out[1].x = RADAR_TILE_SIZE * (x + 1); + out[1].y = RADAR_TILE_SIZE * (y - 1); + + // top right + out[2].x = RADAR_TILE_SIZE * (x + 1); + out[2].y = RADAR_TILE_SIZE * (y); + + // top left + out[3].x = RADAR_TILE_SIZE * (x); + out[3].y = RADAR_TILE_SIZE * (y); } #endif -void CRadar::ClipRadarTileCoords(int *x, int *y) +void CRadar::ClipRadarTileCoords(int &x, int &y) { - if (*x < 0) - *x = 0; - if (*x > 7) - *x = 7; - if (*y < 0) - *y = 0; - if (*y > 7) - *y = 7; + if (x < 0) + x = 0; + if (x > RADAR_NUM_TILES-1) + x = RADAR_NUM_TILES-1; + if (y < 0) + y = 0; + if (y > RADAR_NUM_TILES-1) + y = RADAR_NUM_TILES-1; } STARTPATCHES +// InjectHook(0x4A3EF0, CRadar::Initialise, PATCH_JUMP); +// InjectHook(0x4A3F60, CRadar::Shutdown, PATCH_JUMP); +// InjectHook(0x4A4030, CRadar::LoadTextures, PATCH_JUMP); +// InjectHook(0x4A4180, CRadar::GetNewUniqueBlipIndex, PATCH_JUMP); +// InjectHook(0x4A41C0, CRadar::GetActualBlipArrayIndex, PATCH_JUMP); + InjectHook(0x4A4200, CRadar::DrawMap, PATCH_JUMP); + InjectHook(0x4A42F0, CRadar::DrawBlips, PATCH_JUMP); +// InjectHook(0x4A4C70, CRadar::Draw3dMarkers, PATCH_JUMP); + InjectHook(0x4A4F30, CRadar::LimitRadarPoint, PATCH_JUMP); + InjectHook(0x4A4F90, CRadar::CalculateBlipAlpha, PATCH_JUMP); InjectHook(0x4A5040, CRadar::TransformRadarPointToScreenSpace, PATCH_JUMP); + InjectHook(0x4A50D0, CRadar::TransformRealWorldPointToRadarSpace, PATCH_JUMP); + InjectHook(0x4A5300, CRadar::TransformRadarPointToRealWorldSpace, PATCH_JUMP); + InjectHook(0x4A5530, CRadar::TransformRealWorldToTexCoordSpace, PATCH_JUMP); +// InjectHook(0x4A5590, CRadar::SetCoordBlip, PATCH_JUMP); +// InjectHook(0x4A5640, CRadar::SetEntityBlip, PATCH_JUMP); + InjectHook(0x4A56C0, CRadar::ClearBlipForEntity, PATCH_JUMP); +// InjectHook(0x4A5720, CRadar::ClearBlip, PATCH_JUMP); +// InjectHook(0x4A5770, CRadar::ChangeBlipColour, PATCH_JUMP); +// InjectHook(0x4A57A0, CRadar::ChangeBlipBrightness, PATCH_JUMP); +// InjectHook(0x4A57E0, CRadar::ChangeBlipScale, PATCH_JUMP); +// InjectHook(0x4A5810, CRadar::ChangeBlipDisplay, PATCH_JUMP); +// InjectHook(0x4A5840, CRadar::SetBlipSprite, PATCH_JUMP); + InjectHook(0x4A5870, CRadar::ShowRadarTrace, PATCH_JUMP); + InjectHook(0x4A59C0, CRadar::ShowRadarMarker, PATCH_JUMP); + InjectHook(0x4A5BB0, CRadar::GetRadarTraceColour, PATCH_JUMP); + InjectHook(0x4A5C60, CRadar::SetRadarMarkerState, PATCH_JUMP); + InjectHook(0x4A5D10, CRadar::DrawRotatingRadarSprite, PATCH_JUMP); + InjectHook(0x4A5EF0, CRadar::DrawRadarSprite, PATCH_JUMP); +// InjectHook(0x4A60E0, CRadar::RemoveRadarSections, PATCH_JUMP); + InjectHook(0x4A6100, CRadar::StreamRadarSections, PATCH_JUMP); + InjectHook(0x4A64A0, CRadar::ClipRadarPoly, PATCH_JUMP); + InjectHook(0x4A67E0, CRadar::DrawRadarSection, PATCH_JUMP); + InjectHook(0x4A69C0, CRadar::DrawRadarMask, PATCH_JUMP); +// InjectHook(0x4A6B60, CRadar::StreamRadarSections, PATCH_JUMP); + InjectHook(0x4A6C20, CRadar::DrawRadarMap, PATCH_JUMP); +// InjectHook(0x4A6E30, CRadar::SaveAllRadarBlips, PATCH_JUMP); +// InjectHook(0x4A6F30, CRadar::LoadAllRadarBlips, PATCH_JUMP); + + InjectHook(0x4A61C0, CRadar::GetTextureCorners, PATCH_JUMP); + InjectHook(0x4A6160, IsPointInsideRadar, PATCH_JUMP); + InjectHook(0x4A6250, LineRadarBoxCollision, PATCH_JUMP); ENDPATCHES diff --git a/src/Radar.h b/src/Radar.h index 9e687e24..dec07667 100644 --- a/src/Radar.h +++ b/src/Radar.h @@ -100,7 +100,7 @@ public: static void Draw3dMarkers(); static void DrawMap(); static void StreamRadarSections(int x, int y); - static int ClipRadarPoly(CVector2D *out, CVector2D *in); + static int ClipRadarPoly(CVector2D *out, const CVector2D *in); static void DrawRadarSection(int x, int y); static void RequestMapSection(int x, int y); static void RemoveMapSection(int x, int y); @@ -116,10 +116,12 @@ public: static void DrawRadarMask(); static void SetRadarMarkerState(int counter, int flag); static bool DisplayThisBlip(int counter); - static void GetTextureCorners(int x, int y, CVector2D * out); - static void ClipRadarTileCoords(int *x, int *y); static void TransformRealWorldToTexCoordSpace(CVector2D &out, const CVector2D &in, int x, int y); static void TransformRadarPointToRealWorldSpace(CVector2D &out, const CVector2D &in); static void TransformRadarPointToScreenSpace(CVector2D &out, const CVector2D &in); static void TransformRealWorldPointToRadarSpace(CVector2D &out, const CVector2D &in); + + // no in CRadar in the game: + static void GetTextureCorners(int x, int y, CVector2D *out); + static void ClipRadarTileCoords(int &x, int &y); }; diff --git a/src/RwHelper.cpp b/src/RwHelper.cpp index 5aa31e92..3c198272 100644 --- a/src/RwHelper.cpp +++ b/src/RwHelper.cpp @@ -108,6 +108,23 @@ GetFirstAtomic(RpClump *clump) return atm; } +RwTexture* +GetFirstTextureCallback(RwTexture *tex, void *data) +{ + *(RwTexture**)data = tex; + return nil; +} + +RwTexture* +GetFirstTexture(RwTexDictionary *txd) +{ + RwTexture *tex; + + tex = nil; + RwTexDictionaryForAllTextures(txd, GetFirstTextureCallback, &tex); + return tex; +} + void CameraSize(RwCamera * camera, RwRect * rect, RwReal viewWindow, RwReal aspectRatio) diff --git a/src/RwHelper.h b/src/RwHelper.h index e0ec00a3..ef20467d 100644 --- a/src/RwHelper.h +++ b/src/RwHelper.h @@ -7,6 +7,7 @@ void DefinedState(void); RwFrame *GetFirstChild(RwFrame *frame); RwObject *GetFirstObject(RwFrame *frame); RpAtomic *GetFirstAtomic(RpClump *clump); +RwTexture *GetFirstTexture(RwTexDictionary *txd); RwTexDictionary *RwTexDictionaryGtaStreamRead(RwStream *stream); RwTexDictionary *RwTexDictionaryGtaStreamRead1(RwStream *stream); -- cgit v1.2.3