more Radar code

This commit is contained in:
aap 2019-06-23 19:59:58 +02:00
parent a5ba538968
commit 4d84d94166
4 changed files with 352 additions and 40 deletions

View File

@ -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
@ -499,6 +706,39 @@ void CRadar::TransformRealWorldPointToRadarSpace(CVector2D &out, const CVector2D
}
#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); }
#else
@ -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

View File

@ -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);
};

View File

@ -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)

View File

@ -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);